Кластеризация текста в Python

Мне нужно сгруппировать некоторые текстовые документы, и я изучаю различные варианты. Похоже, что LingPipe может кластеризовать простой текст без предварительного преобразования (в векторное пространство и т. д.), но это единственный инструмент, который я видел, который явно заявляет, что работает со строками.

Существуют ли какие-либо инструменты Python, которые могут напрямую кластеризовать текст? Если нет, то как лучше всего справиться с этим?


person Dan    schedule 24.11.2009    source источник
comment
Информация о кластеризации текста: www2.parc.com/istl/projects /ia/sg-clustering.html   -  person fviktor    schedule 24.11.2009
comment
Если вам нужна помощь в извлечении текстового содержимого из различных типов документов, пожалуйста, добавьте еще один вопрос, так как я думаю, что это еще одна часть задачи. ИМХО, это позволило бы лучше разделить проблемы.   -  person fviktor    schedule 24.11.2009
comment
Mallet также будет работать с текстовыми файлами без каких-либо действий, при условии, что у вас есть полный каталог (с одним документом на файл). Я рекомендую просто использовать nltk, библиотеку Python. Вам нужно будет сделать небольшую предварительную обработку файлов, но это не больно.   -  person ealdent    schedule 24.11.2009


Ответы (3)


Качество текстовой кластеризации зависит в основном от двух факторов:

  1. Некоторое понятие сходства между документами, которые вы хотите сгруппировать. Например, легко различить новостные статьи о спорте и политике в векторном пространстве с помощью tfidf-cosine-distance. Гораздо сложнее разделить обзоры продуктов на «хорошие» или «плохие» на основе этого показателя.

  2. Сам метод кластеризации. Вы знаете, сколько кластеров будет? Хорошо, используйте kmeans. Вы не заботитесь о точности, но хотите показать красивую древовидную структуру для навигации по результатам поиска? Используйте какую-либо иерархическую кластеризацию.

Не существует решения для кластеризации текста, которое бы хорошо работало при любых обстоятельствах. И поэтому, вероятно, недостаточно взять какое-то программное обеспечение для кластеризации из коробки и бросить в него свои данные.

Сказав это, вот некоторый экспериментальный код, который я использовал некоторое время назад, чтобы поиграть с текстовой кластеризацией. Документы представлены в виде нормализованных tfidf-векторов, а сходство измеряется как косинусное расстояние. Сам метод кластеризации — основной кластер.

import sys
from math import log, sqrt
from itertools import combinations

def cosine_distance(a, b):
    cos = 0.0
    a_tfidf = a["tfidf"]
    for token, tfidf in b["tfidf"].iteritems():
        if token in a_tfidf:
            cos += tfidf * a_tfidf[token]
    return cos

def normalize(features):
    norm = 1.0 / sqrt(sum(i**2 for i in features.itervalues()))
    for k, v in features.iteritems():
        features[k] = v * norm
    return features

def add_tfidf_to(documents):
    tokens = {}
    for id, doc in enumerate(documents):
        tf = {}
        doc["tfidf"] = {}
        doc_tokens = doc.get("tokens", [])
        for token in doc_tokens:
            tf[token] = tf.get(token, 0) + 1
        num_tokens = len(doc_tokens)
        if num_tokens > 0:
            for token, freq in tf.iteritems():
                tokens.setdefault(token, []).append((id, float(freq) / num_tokens))

    doc_count = float(len(documents))
    for token, docs in tokens.iteritems():
        idf = log(doc_count / len(docs))
        for id, tf in docs:
            tfidf = tf * idf
            if tfidf > 0:
                documents[id]["tfidf"][token] = tfidf

    for doc in documents:
        doc["tfidf"] = normalize(doc["tfidf"])

def choose_cluster(node, cluster_lookup, edges):
    new = cluster_lookup[node]
    if node in edges:
        seen, num_seen = {}, {}
        for target, weight in edges.get(node, []):
            seen[cluster_lookup[target]] = seen.get(
                cluster_lookup[target], 0.0) + weight
        for k, v in seen.iteritems():
            num_seen.setdefault(v, []).append(k)
        new = num_seen[max(num_seen)][0]
    return new

def majorclust(graph):
    cluster_lookup = dict((node, i) for i, node in enumerate(graph.nodes))

    count = 0
    movements = set()
    finished = False
    while not finished:
        finished = True
        for node in graph.nodes:
            new = choose_cluster(node, cluster_lookup, graph.edges)
            move = (node, cluster_lookup[node], new)
            if new != cluster_lookup[node] and move not in movements:
                movements.add(move)
                cluster_lookup[node] = new
                finished = False

    clusters = {}
    for k, v in cluster_lookup.iteritems():
        clusters.setdefault(v, []).append(k)

    return clusters.values()

def get_distance_graph(documents):
    class Graph(object):
        def __init__(self):
            self.edges = {}

        def add_edge(self, n1, n2, w):
            self.edges.setdefault(n1, []).append((n2, w))
            self.edges.setdefault(n2, []).append((n1, w))

    graph = Graph()
    doc_ids = range(len(documents))
    graph.nodes = set(doc_ids)
    for a, b in combinations(doc_ids, 2):
        graph.add_edge(a, b, cosine_distance(documents[a], documents[b]))
    return graph

def get_documents():
    texts = [
        "foo blub baz",
        "foo bar baz",
        "asdf bsdf csdf",
        "foo bab blub",
        "csdf hddf kjtz",
        "123 456 890",
        "321 890 456 foo",
        "123 890 uiop",
    ]
    return [{"text": text, "tokens": text.split()}
             for i, text in enumerate(texts)]

def main(args):
    documents = get_documents()
    add_tfidf_to(documents)
    dist_graph = get_distance_graph(documents)

    for cluster in majorclust(dist_graph):
        print "========="
        for doc_id in cluster:
            print documents[doc_id]["text"]

if __name__ == '__main__':
    main(sys.argv)

Для реальных приложений вы должны использовать приличный токенизатор, использовать целые числа вместо строк токенов и не вычислять матрицу расстояний O (n ^ 2)...

person ephes    schedule 24.11.2009

Это кажется возможным, если использовать простые инструменты командной строки UNIX для извлечения текстового содержимого этих документов в текстовые файлы, а затем использовать чистое решение Python для фактической кластеризации.

Я нашел фрагмент кода для кластеризации данных в целом:

http://www.daniweb.com/code/snippet216641.html

Пакет Python для этого:

http://python-cluster.sourceforge.net/

Другой пакет Python (используется в основном для биоинформатики):

http://bonsai.ims.u-tokyo.ac.jp/~mdehoon/software/cluster/software.htm#pycluster

person fviktor    schedule 24.11.2009

Существует библиотека Python NLTK, которая поддерживает лингвистический анализ, включая кластеризацию текста.

person lynxoid    schedule 25.04.2012