Программирование >>  Рекурсивные объекты и фрактальные узоры 

1 ... 22 23 24 [ 25 ] 26 27 28 ... 43


Для воплощения в жизнь второго наблюдения нам потребуются ещё две формулы:

df = количество документов, содержащих слово i

idf. = logj (N / df), где N - общее количество документов в коллекции

Величина idf, называемая обратной документной частотой (inverse document frequency, в русскоязычной литературе термин ещё не устоялся), тем выше, чем меньше в коллекции файлов, содержащих слово i.

Итоговый вес слова i в документе] определяется по формуле:

W, =tf.*idf

Ч Ч

Если вы не забыли, определение веса слов документа - лишь часть нашей задачи. Теперь необходимо решить, каким образом будет производиться вычисление функции подобия для векторов D. и D.. Здесь всё просто, хотя и громоздко:

т *=1

Не забывайте, что любое слово в коллекции можно закодировать числом от единицы до Т.

Поскольку документы представляются в виде векторов, мы можем применить известную в математике формулу скалярного произведения (числитель дроби). Знаменатель нормализует полученный результат, загоняя его в промежуток [0,1].

Модель векторного пространства - алгоритм во многом эвристический, критика в его адрес всегда существовала. Хотя основная цель задачи состоит в реализации систем поиска и рубрикации, разработка собственной функции близости также весьма приветствуется.

6.1.3. Определение авторства Классификация произведений различных авторов

Предположим, некто содержит большую библиотеку электронных текстов. Благодарные читатели со всего света посылают владельцу библиотеки txt-версии понравившихся им книг, пополняя основную коллекцию.

Допус ТИМ, один из читателей прислал весьма интересный рассказ, но вот незадача - забыл указать (или просто не знает) его автора. Скорее всего, в

Здесь Т - размер словаря, то есть общее количество различных слов (поскольку речь идёт о текстовых документах, под словами подразумеваются обычные слова русского языка), встречающихся в документах коллекции. Числа w,., Wjj,w. символизируют веса (или значимость ) отдельных слов в документе D..

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

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

Вес зависит также от наибольшей частоты слова, найденной в процессе анализа документа. Если слово принтер встретилось в тексте десять раз - это много или мало? Зависит от частоты других слов. Например, если самое популярное слово ( винчестер ) использовалось пятнадцать раз, то вес терма принтер в данном тексте достаточно велик. Если же слово винчестер встретилось сорок раз, то текст относится скорее к винчестерам, чем к принтерам, и десятикратное вхождение слова принтер уже не впечатляет.

Используя стандартные обозначения, это наблюдение можно записать с помощью формул:

fj. = частота (количество вхождений) слова в документе]

Ц,-у шах, (У

Последняя формула подводит итог первому наблюдению: вес слова i в документе j (tf.) пропорционален его частоте и обратно пропорционален максимальной частоте других слов, обнаруженной в документе j.

Наблюдение второе. Вес слова снижается, если оно часто встречается во всей коллекции.

Например, слово принтер довольно редко встречается в текстах, посвященных автомобилям. Поэтому искать документ по слову принтер имеет смысл: вряд ли поисковая система завалит вас тоннами содержащих его текстов. С другой стороны, если все документы коллекции посвящены компьютерной технике, запрос принтер даст мало что: вероятно, каждый второй текст содержит это слово. Конечно, любой документ, в котором говорится о принтерах, будет соответствовать запросу, но в данном случае поиск будет напоминать розыск преступника по фотороботу, обычно похожему на всех людей в мире и ни на кого персонально.



Запрограммировать метод KNN - дело техники, поэтому займёмся сначала самым важным - функцией расстояния. В её основе лежит простое наблюдение: даже в схожих ситуациях разные люди склонны употреблять разные слова и разные словосочетания. Это, по-видимому, в особенности относится к словосочетаниям. Вероятно, описывая солнце, корову или табуретку, большинство писателей так и напишут: солнце , корова , табуретка . В словосочетаниях простора для фантазии больше. Говоря об одном и том же летнем дне, можно сказать горячее солнце , пекущее солнце , жаркое солнце ... Разные авторы подберут разные эпитеты. Если же в двух различных произведениях наблюдается сравнительно частое использование одних и тех же словосочетаний, можно сделать вывод: гфоизведения принадлежат перу одного и того же писателя.

Практическая реализация функции расстояния навеяна методикой вычисления подобия документов из задачи поисковая система и рубрикатор , но следует ей лишь частично.

Первым делом любой паре слов (wl, w2) каждого документа коллекции сопоставляется вещественное число - вероятность встретить слово w2 непосредственно за словом wl. Например, если в анализируемом документе слово солнце встречается после слова жаркое в каждом пятом случае, то паре (жаркое, солнце) сопоставляется число 1/5.

Далее для двух сравниваемых документов определяется список пар слов, найденных в каждом документе по отдельности. Если в одном документе пара жаркое солнце найдена, а в другом нет, она не будет участвовать в вычислении расстояния.

Соответствующие парам вероятности записываются в два различных вектора (вектор первого документа D1, вектор второго документа D2), после чего расстояние вычисляется с помощью формулы нормализованного скалярного произведения:

Distance(Dj, D) = 1 - (D D,) / IIDJI IIDJI

Теперь приступим к программированию. Здесь в первую очередь потребуется реализация алгоритма KNN. Функция Classify() принимает в качестве параметров новый элемент коллекции, описание существующих кластеров и число N, а возвращает номер кластера, соответствующий классифицируемому элементу Описание кластеров представляет собой вектор списков элементов. К-й элемент вектора является К-м кластером коллекции.

служебная функция для сортировки пар (расстояние, кластер) bool Less(pair<double, int> Ihs, pair<double, int> rhs) { return Ihs.first < rhs.first; }

библиотеке уже есть произведения этого писателя, но поможет ли наличие существующих текстов определить авторство вновь поступившего рассказа?

Эта задача входит в компетенцию методов стилометрии, то есть компьютерного анализа стиля писателя. Как и в задаче информационного поиска, главное здесь - изобретение адекватной метрики для множества документов. Имея кластеризованную (распределённую по авторам) коллекцию и метрику, с помощью метода KNN можно найти наиболее подходящий кластер для нового текстового файла.

В предыдущей задаче функция подобия sim () считала похожими документы, похожие по содержанию. 11а сей раз содержание пе играет никакой роли - важен стиль изложения, язык автора.

Хотя понятие стиля на первый взгляд кажется неуловимым, в действительности из текста можно извлечь довольно много информации, так или иначе характеризующей его создателя:

разные авторы обычно используют предложения разной длины;

лексиконы писателей различаются (особенно интересно изучить любимые слова - то есть слова, в целом по коллекции встречающиеся гораздо реже, чем у данного конкретного автора);

в дополнение к частоте использования отдельных слов, интересно иззить употребление писателем словосочетаний, состоящих из двух-трёх слов.

Это только некоторые идеи, которые могут лечь в основу хорошей метрики. Идеального же алгоритма, отличающегося стопроцентным попаданием, на сегодняшний день не существует.

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

Решение

Сразу оговорюсь: решение, которое здесь приводится, очень далеко от совершенства. Придумать хорошую метрику для задачи определения авторства, пожалуй, не проще, чем создать сильную шахматную программу. Мой подход - всего лишь возможная стартовая точка для гораздо более серьёзных методов. Впрочем, для использовавшейся небольшой тестовой коллекции он показал вполне удовлетворительные результаты.



определить номер победившего кластера

return max element(Votes.begin() , Votes.end()) - Votes.begin();

Следующий этап - реализация типа данных документ и функции расстояния:

struct Document {

сопоставляет паре (wl, w2) вероятность появления w2 после wl map<pair<string, string>, double> Prob;

загрузить документ из файла Document(const string filename) {

map<pair<string, string>, int> Freq; частота пары (wl, w2) map<string, int> Count; частота слова wl

ifstream file(filename.c str()); string wl, w2;

file wl;

while(file w2) пока не конец файла

{ обновить Count и Freq

Count[wl]++;

Freq[make pair(wl, w2)]++;

wl = w2;

преобразовать частоту в вероятность:

вероятность встретить слово w2 после wl равна

частоте встречаемости пары (wl, w2),

делённой на частоту встречаемости слова wl

for(tnap<pair<string, string>, int>:: iterator p = Freq.begin {);

p 1= Freq.endO ; p+ + ) Prob[p->first] = p->second / double(Count[p->first.first]);

double Distance (const Documents dl, const DocumentSc d2) {

список словосочетаний, встречающихся в обоих документах set<pair<string, string> > Pairs;

цикл по всем парам первого документа

for(map<pair<string, string>, double>::const iterator p = dl.Prob.beginO;p != dl.Prob.end(); p++) если пара найдена и во втором документе if(d2.Prob.find(p->first) != d2.Prob.end()) Pairs.insert(p->first);

если документы вообще не пересекаются, расстояние между ними максимально if(Pairs.empty О) return 1;

вычислить скалярное произведение векторов (Р) double Р = 0;

for(set<pair<string, string> >:riterator p = Pairs.begin();

p 1= Pairs.endO; p++) P += dl.Prob.find(*p)->second * d2.Prob.find(*p)->second;

вычислить квадрат нормы каждого из векторов double si = О, s2 = 0;

for(set<pair<string, string> >::iterator p = Pairs.begin();

p != Pairs.endO; p++)

si += dl.Prob.find(*p)->second * dl.Prob.find(*p)->second; s2 += d2.Prob.find(*p)->second * d2.Prob.find(*p)->second;

return 1 - P / (sqrt(sl)*sqrt(s2));

template <class Туре>

int Classify(const Туре& Object, const vector<list<Type> >& Clusters, int N) {

список голосующих элементов (расстояние, номер кластера) list<pair<double, int> > VotingList;

для каждого элемента коллекции *р вычислить расстояние до Object и поместить пару (расстояние, кластер элемента) в список VotingList

for(unsigned i = 0; i < Clusters.size(); i++)

for(list<Type>::const iterator p = Clusters[i].begin();

p != Clusters [i] .endO ; p++) VotingList.push back(pair<double, int>(Distance(Object, *p), i));

отсортировать список по возрастанию расстояния VotingList.sort(Less);

vector<int> Votes(Clusters.size());

list<pair<double, int> >::iterator p = VotingList.begin(); подсчитать количество голосов первых N членов (Votes[к] - количество голосов за кластер к) for(int i = 0; i < N; i++)

{ Votes[p->second]++; p++; }



1 ... 22 23 24 [ 25 ] 26 27 28 ... 43

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика