предисловие
Цель этой статьи состоит в том, чтобы зафиксировать опыт и опыт Бена Менсиня в кратком изложении проекта по тренировке рук.В основном для начинающих, представленные концепции и технологии будут более базовыми, чтобы дать некоторые идеи для решения практических задач (не обязательно придерживаться используемых в них концепций и алгоритмов, можно сделать лучше в деталях, и заменить их другими более продвинутые и передовые технологии) и сосредоточусь на технических деталях, которые, как мне кажется, требуют большего внимания.
Уведомление
- Демонстрационные данные в этой статье не могут быть опубликованы. При необходимости просканируйте их самостоятельно.
- Основные операции здесь не обсуждаются, при необходимости обращайтесь к соответствующим документам.
- Связанные понятия:TF-IDF,наивный байес,кластеризация k-средних.
описание проблемы
Если вы сотрудник информационного агентства в Китае, и вы обнаружили, что другие СМИ плагиатят статьи на вашей платформе, теперь вы получили задание выяснить статьи, подозреваемые в плагиате другими СМИ, и сравнить их с оригиналом текст, чтобы найти место плагиата.
Процесс решения
1. Очистка данных
Сначала мы читаем данные, названные какnews
Кадр данных, поля данных примерно следующие
id | author | source | content | feature | title | url |
---|---|---|---|---|---|---|
89617 | NaN | Быстрая технология | Кроме того, с этой недели (12 июня), за исключением 15 моделей, таких как Xiaomi Mi 6, остальные модели были приостановлены для выпуска обновлений (включая версию для разработки / | {"тип":"Технология","сайт":"cnbeta","commentNum":"37"... | Представлена первая партия моделей Xiaomi MIUI 9: всего 15 моделей | http://www.cnbeta.com/articles/tech/623597.htm |
89616 | NaN | Быстрая технология | Snapdragon 835 — единственный процессор ARM, сертифицированный для настольной платформы Windows 10. | {"тип":"Технология","сайт":"cnbeta","commentNum":"15"... | Ожидается, что производительность Snapdragon 835 в Windows 10 улучшится | http://www.cnbeta.com/articles/tech/623599.htm |
89613 | Ху Шули_MN7479 | Шэньчжэньское мероприятие | (Оригинальное название: 44-летней женщине отказали в свидании с пользователем сети в Шэньчжэне, и она побежала голой в ливень...)\r\n@Шэньчжэньская дорожная полиция Weibo сообщила: Вчера ясно .. | {"type":"Новости","site":"Netease Popular","commentNum":"978",.. | 44-летняя женщина попросила пользователя сети пробежаться голой под ливнем, а сотрудники ГИБДД сопровождали ее в одежде. | http://news.163.com/17/0618/00/CN617P3Q0001875... |
Нам нужно обучить модель в соответствии с полем содержимого, поэтому посмотрите образцы, поле содержимого которых равно NaN, После просмотра не так много образцов, поэтому его можно удалить напрямую.
#show nans in the dataset
news[news.content.isna()].head(5)
#drop the nans
news=news.dropna(subset=['content'])
Затем определите простую функцию (используя сегментацию слов jieba), чтобы подготовиться к сегментации слов содержимого, удалите некоторые символы и китайскую пунктуацию перед сегментацией слов и отфильтруйте некоторые стоп-слова после сегментации слов, среди которыхpunctuation
Содержит все китайские знаки препинания,stopwords
представляет собой список, содержащий некоторые стоп-слова (поиск Baidu можно скачать, также можно отредактировать по мере необходимости). Здесь я просто показываю возможный метод обработки. Если вы считаете, что есть возможности для улучшения, вам не нужно этого делать. Может быть, вы можете использовать pos тега для фильтрации нужных слов в соответствии с частью речи, или вам нужно определение фразы или даже word2vec для ее представления.
def split_text(text):return ' '.join([w for w in list(jieba.cut(re.sub('\s|[%s]' % (punctuation),'',text))) if w not in stopwords])
Тестовая функция, вероятно, выглядит так:
split_text(news.iloc[1].content)
#out:
'''骁龙 835 唯一 Windows10 桌面 平台 认证 ARM 处理器 高通 强调 不会 只 考虑 性能 屏蔽掉 小 核心 相反 正 联手 微软 找到 一种 适合 桌面 平台 兼顾 性能 功耗 完美 方案 报道 微软 已经 拿到 一些 源码 Windows10 更好 理解 big little 架构 资料 显示 骁龙 835 一款 集成 CPUGPU 基带 蓝牙 Wi Fi SoC 传统 Wintel 方案 节省 至少 30% PCB 空间 按计划 今年 Q4 华硕 惠普 联想 首发 骁龙 835Win10 电脑 预计 均 二合一 形态 产品 当然 高通 骁龙 未来 也许 见到 三星 Exynos 联发科 华为 麒麟 小米 澎湃 进入 Windows10 桌面 平台'''
Теперь вы можете применить эту функцию ко всему полю содержимого столбца! Здесь показан метод с использованием pandas, а в полном примере кода я использую более pythontic метод.
news['content_split'] = news['content'].apply(split_text)
Точно так же мы можем использовать аналогичный метод для создания ярлыков (например, теперь я предполагаю, что источники новостей содержат слово Синьхуа в качестве положительного примера).
news['is_xinhua'] = np.where(news['source'].str.contains('新华'), 1, 0)
На этом наша работа по очистке данных завершена! :D
2. Предварительная обработка данных
Чтобы использовать алгоритм машинного обучения, мы должны преобразовать текст в форму, которую алгоритм может понять, теперь нам нужно использовать конструкции sklearn.TF-IDFМатрица для представления текста, TF-IDF — это простой и эффективный способ представления текста, если вы не знаете, что это такое, перейдите по ссылке.
tfidfVectorizer = TfidfVectorizer(encoding='gb18030',min_df=0.015)
tfidf = tfidfVectorizer.fit_transform(news['content_split'])
при созданииTfidfVectorizerОбратите внимание на указание параметра кодировки (по умолчанию utf-8), где min_df=0.015 означает, что словарь с частотой документа ниже установленного порога игнорируется при создании тезауруса, эта настройка связана с тем, что моя машина не может вычислить слишком много особенности, если вычислительных ресурсов достаточно установить max_features=30000, что примет слова, частота слов которых входит в число 30000 лучших, в качестве функции (столбец матрицы tfidf), чтобы эффект модели был лучше.
3. Обучите модель прогнозирования
Перед обучением модели нам нужно разделить данные на обучающий набор (70%) и тестовый набор (30%).
#split the data
lable = news['is_xinhua'].values
X_train, X_test, y_train, y_test = train_test_split(tfidf.toarray(),label,test_size = 0.3, random_state=42)
Теперь вы можете обучать свою модель с помощью Наивного Байеса!
clf = MultinomialNB()
clf.fit(X=X_train,y=y_train)
Теперь, как мы узнаем, хорошо ли подходит наша модель? может быть примененоперекрестная проверкаВыведите метрики, которые вас интересуют.Здесь я выбираю точность, полноту, точность и f1 для 3-кратной перекрестной проверки (на самом деле вам нужно выбрать разные метрики, основанные на ваших проблемах, если вы не знаете эти метрики, обязательно чтобы ознакомиться с соответствующими материалами.) и сравнить с производительностью тестового набора.
scores=cross_validate(clf,X_train,y_train,scoring=('precision','recall','accuracy','f1',cv=3,return_train_score=True)
print(scores)
#out:
'''{'fit_time': array([0.51344204, 0.43621135, 0.40280986]),
'score_time': array([0.15626907, 0.15601063, 0.14357495]),
'test_precision': array([0.9599404 , 0.96233543, 0.96181975]),
'train_precision': array([0.96242476, 0.96172716, 0.96269257]),
'test_recall': array([0.91072205, 0.91409308, 0.90811222]),
'train_recall': array([0.91286973, 0.91129295, 0.91055894]),
'test_accuracy': array([0.88475361, 0.88981883, 0.88415715]),
'train_accuracy': array([0.88883419, 0.88684308, 0.88706462]),
'test_f1': array([0.93468374, 0.93759411, 0.9341947 ]),
'train_f1': array([0.93699249, 0.93583104, 0.9359003 ])}'''
y_predict = clf.predict(X_test)
def show_test_reslt(y_true,y_pred):
print('accuracy:',accuracy_score(y_true,y_pred))
print('precison:',precision_score(y_true,y_pred))
print('recall:',recall_score(y_true,y_pred))
print('f1_score:',f1_score(y_true,y_pred))
show_test_reslt(y_test,y_predict)
#out:
'''
accuracy: 0.8904162040050542
precison: 0.9624150339864055
recall: 0.9148612694792855
f1_score: 0.9380358534684333
'''
Прежде всего, посмотрите на результаты cv. Показатели 3-кратного измерения не сильно отличаются и относительно стабильны, а результаты тестового набора и cv также очень похожи, что указывает на то, что эффект подгонки модели приемлем. функции в этих данных, точность может быть улучшена близко к 1.
На данный момент мы установили модель, которая предсказывает, является ли источник новостной платформой по тексту, и теперь мы можем находить статьи с плагиатом.
4. Найдите статьи с плагиатом
На этом этапе мы можем предсказать полный текст (или новый добавленный текст, вам может потребоваться инкапсулировать конвейер при его использовании, что не будет продемонстрировано здесь) в соответствии с результатами, предсказанными моделью. на самом деле отрицательный. Это означает, что их тексты похожи на стиль письма вашей платформы, прежде чем они будут ошибочно оценены. Эти тексты, скорее всего, являются плагиатом или оригинальными цитатами. Во-первых, удалите эту часть «кандидатов».
prediction = clf.predict(tfidf.toarray())
labels = np.array(label)
compare_news_index = pd.DataFrame({'prediction':prediction,'labels':labels})
copy_news_index=compare_news_index[(compare_news_index['prediction'] == 1) & (compare_news_index['labels'] == 0)].index
xinhuashe_news_index=compare_news_index[(compare_news_index['labels'] == 1)].index
Теперь мы должны сравнить эти подозреваемые в плагиате тексты с исходным текстом, и взять тексты с высоким сходством для дальнейшего анализа, но если алгоритм поиска методом полного перебора достаточно сложен, всего два вложенных цикла уже O(n ^ 2), этот подход слишком неэффективен.
Поэтому нам нужен более эффективный способ поиска похожего текста, здесь я использую кластеризацию k-средних (конечно, есть методы получше, вы можете улучшить). Сначала выполните кластеризацию k-средних для всех текстов, мы можем получить словарь id-кластера и создать словарь кластера-идентификатора на основе этого словаря, чтобы, учитывая конкретный текст, я мог знать, к какому кластеру этот текст принадлежит, а затем используйте его Сравните с другими текстами в кластере, чтобы найти наиболее похожие первые n текстов для повторного анализа, что значительно сокращает диапазон поиска.
normalizer = Normalizer()
scaled_array = normalizer.fit_transform(tfidf.toarray())
kmeans = KMeans(n_clusters=25,random_state=42,n_jobs=-1)
k_labels = kmeans.fit_predict(scaled_array)
id_class = {index:class_ for index,class_ in enumerate(k_labels)}
class_id = defaultdict(set)
for index,class_ in id_class.items():
if index in xinhuashe_news_index.tolist():
class_id[class_].add(index)
Здесь следует отметить, что алгоритм k-средних в sklearn поддерживает только расчет сходства на основе евклидова расстояния.При сравнении сходства между текстом и текстом мы обычно используем косинусное расстояние.Перед использованием k-средних нам нужно преобразовать Матрица tfidf нормализуется к единичной норме, потому что евклидовы и косинусные расстояния после этого линейно связаны (почему?Смотри сюда), так что косинусное расстояние используется для измерения сходства при кластеризации.
Еще один момент, о котором следует поговорить, — это выбор количества центров k-средних (n_clusters), здесь я выбираю просто кластеризацию в 25 классов. На самом деле, вы можете выбрать количество центров на основе ваших знаний о данных.Например, если вы знаете, что ваши данные содержат новости о спорте, военных и развлечениях, вы можете выбрать количество центров на основе вашего опыта. Конечно, предполагается, что вы хорошо знакомы с данными. Другой метод заключается в наблюдении за значением изгиба и выборе количества центров в соответствии с некоторыми показателями, такими как SSE, силуэт и т. д.Подробный пример здесь.
Теперь мы можем применить результаты кластеризации для поиска похожих текстов.
def find_similar_text(cpindex,top=10):
dist_dict={i:cosine_similarity(tfidf[cpindex],tfidf[i]) for i in class_id[id_class[cpindex]]}
return sorted(dist_dict.items(),key=lambda x:x[1][0],reverse=True)[:top]
print(copy_news_index.tolist())
#random choice a candidate to show some results
fst=find_similar_text(3352)
print(fst)
#out:
'''
id , cosine_similarity
[(3134, array([[0.96849349]])),
(63511, array([[0.94619604]])),
(29441, array([[0.94281928]])),
(3218, array([[0.87620818]])),
(980, array([[0.87535143]])),
(29615, array([[0.86922775]])),
(29888, array([[0.86194742]])),
(64046, array([[0.85277668]])),
(29777, array([[0.84882241]])),
(64758, array([[0.73406445]]))]
'''
После нахождения похожих текстов, более внимательно, можно разделить предложения текста по определенным признакам (конкретная длина, конкретный разделитель), или здесь я просто разбиваю текст с помощью ".", а разницу между предложениями похожего текста вычисляю отдельноedit distanceПостсортировка находит определенные сходства.
def find_similar_sentence(candidate,raw):
similist = []
cl = candidate.strip().split('。')
ra = raw.strip().split('。')
for c in cl:
for r in ra:
similist.append([c,r,editdistance.eval(c,r)])
sort=sorted(similist,key=lambda x:x[2])
for c,r,ed in sort:
if c!='' and r!='':
print('怀疑抄袭句:{0}\n相似原句:{1}\neditdistance:{2}\n'.format(c,r,ed))
find_similar_sentence(news.iloc[3352].content,news.iloc[3134].content)
Суммировать
Эта статья в основном обеспечивает основу идей для решения практических задач.Он разлагает практическую проблему обнаружения плагиата на проблему классификации текста и аналогичную задачу поиска текста.Идея решения практических задач в сочетании с машинным обучением заслуживает упоминания.
В то же время во многих частях этой статьи используются только простые методы. Вдохновленные студенты приветствуют непрерывную оптимизацию. Мои дальнейшие идеи и опыт оптимизации будут продолжать обновляться.
Полный пример кода нажмите здесь
Спасибо
Спасибо за ваше терпение при чтении моей статьи. Комментарии и исправления приветствуются. Я надеюсь общаться с вами и вместе добиваться прогресса.
Спасибо моему советнику господину Гао, а также моим одноклассникам и друзьям, которые активно обсуждали и решили проблему!