任务描述
如果你是某新闻单位工作人员(这里假设source=新华社),为了防止其他媒体抄袭你的文章,做一个抄袭自动检测分析的工具:
一、定义可能抄袭的文章来源
二、与原文对比定位抄袭的地方
数据预处理
本次实验涉及的数据预处理
- 数据清洗,针对content字段为空的情况,进行dropna
- 分词,使用jieba进行分词
- 数据集切分,70%训练集,30%测试集
- 分词并保存分词结果到 corpus.pkl
需求分析及实现路线
需求分析
- 实现对大量文本数据的快速加载和处理
- 有效地提取文本特征以进行抄袭检测
- 使用分类模型预测文章风格是否与源文章一致
- 对疑似抄袭文章进行聚类和相似度比对
- 通过编辑距离进行精确比对确认抄袭情况
实现路线
- 数据加载与预处理:加载数据库导出的文章数据,进行数据清洗、分词等预处理操作。
- 特征提取与分类模型训练:提取TF-IDF特征,并使用分类模型(如MultinomialNB)训练数据集。
- 聚类降维:对全量文本进行聚类降维,减少比对复杂度。
- 相似度比对:基于TF-IDF相似度矩阵找出相似文章,并利用编辑距离进行精确比对。
算法设计如下
数据加载与预处理:
a. 加载包含文本内容的CSV文件(如sqlResult.csv)和停用词表(如chinese_stopwords.txt)。
b. 清洗数据,移除空值(如content字段为空的情况)。
c. 使用分词工具(如jieba)对文本进行分词。
d. 将处理后的分词结果保存到corpus.pkl以便后续使用。
e. 将数据集划分为训练集(70%)和测试集(30%)。
特征提取:
a. 计算文本的TF-IDF特征。
预测文章风格:
a. 使用分类算法(如MultinomialNB)基于文本特征(如TF-IDF)和标签(是否为新华社)进行训练。
寻找可能的抄袭文章:
a. 找到预测标签为1(属于新华社)但实际标签为0的文章。
聚类降维:
a. 如果需要比对的文本数量较大,可以使用k-means算法对文本进行聚类降维,例如将文本分为25个类别。
检索相似文章:
a. 对于一篇可能的抄袭文章,从相同标签中找到对应新华社的文章。
b. 根据TF-IDF相似度矩阵,按相似度从大到小排序,取Top N(如Top 10)相似文章。
计算编辑距离:
a. 使用编辑距离(editdistance)算法计算疑似抄袭文章与原文之间的编辑距离。
逐句比对:
a. 对疑似抄袭文章与原文进行逐句比对,计算每个句子的编辑距离。
模块设计及实现
总体模块设计
使用pandas进行数据预处理、特征工程提取TF-IDF特征、基于MultinomialNB进行分类模型训练,并基于K-Means聚类桶进行分组后使用模型进行结果预测,最后使用编辑距离进行文章对比,总体模块设计如下:
1、数据集加载
导包
import pickle, os
import jieba
import pandas as pd
from icecream import ic
from sklearn.cluster import KMeans
from collections import defaultdict
from sklearn.preprocessing import Normalizer # 把tfidf归一化为单位向量
from sklearn.naive_bayes import MultinomialNB # 多项式贝叶斯模型
from sklearn.model_selection import train_test_split # 拆分数据集
from sklearn.feature_extraction.text import CountVectorizer, TfidfTransformer # 统计词频、计算tfidf
from sklearn.metrics import classification_report # 分类报告 评估模型
from sklearn.metrics.pairwise import cosine_similarity # 计算余弦相似度
import editdistance # 计算编辑距离
加载停用词
with open('dataset/chinese_stopwords.txt','r',encoding='utf-8') as file:
stopwords = [line.strip() for line in file]
stopwords[:10]
[',', '的', '。', '、', '在', '了', '是', '', '“', '”']
加载数据集
# gb18030编码收录的中文字符更全,对中文文档兼容性更好
df = pd.read_csv('dataset/sqlResult.csv',encoding='gb18030')
df.head(2)
id | author | source | content | feature | title | url | |
---|---|---|---|---|---|---|---|
0 | 89617 | NaN | 快科技@http://www.kkj.cn/ | 此外,自本周(6月12日)起,除小米手机6等15款机型外,其余机型已暂停更新发布(含开发版/... | {"type":"科技","site":"cnbeta","commentNum":"37"... | 小米MIUI 9首批机型曝光:共计15款 | http://www.cnbeta.com/articles/tech/623597.htm |
1 | 89616 | NaN | 快科技@http://www.kkj.cn/ | 骁龙835作为唯一通过Windows 10桌面平台认证的ARM处理器,高通强调,不会因为只考... | {"type":"科技","site":"cnbeta","commentNum":"15"... | 骁龙835在Windows 10上的性能表现有望改善 | http://www.cnbeta.com/articles/tech/623599.htm |
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 89611 entries, 0 to 89610
Data columns (total 7 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 id 89611 non-null int64
1 author 79396 non-null object
2 source 89609 non-null object
3 content 87054 non-null object
4 feature 89611 non-null object
5 title 89577 non-null object
6 url 87144 non-null object
dtypes: int64(1), object(6)
memory usage: 4.8+ MB
可以看到,content 存在缺失数据,需要进行缺失值处理
而 source 中也存在缺失数据(无源数据),需要单独去除
2、数据预处理
缺失值处理
# 去除content列存在缺失值的行
df.dropna(subset=['content'],inplace=True)
# 去除
df.dropna(subset=['source'],inplace=True)
df.info()
<class 'pandas.core.frame.DataFrame'>
Index: 87052 entries, 0 to 89610
Data columns (total 7 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 id 87052 non-null int64
1 author 76925 non-null object
2 source 87052 non-null object
3 content 87052 non-null object
4 feature 87052 non-null object
5 title 87052 non-null object
6 url 87052 non-null object
dtypes: int64(1), object(6)
memory usage: 5.3+ MB
分词,使用 jieba 分词
def split_text(text):
text = text.replace(' ', '').replace('\n','').replace('\r','')
text2 = jieba.cut(text)
# 去掉停用词
result = ' '.join([w for w in text2 if w not in stopwords])
return result
temp = df.iloc[0].content
print('测试数据:',temp)
print('测试分词结果:',split_text(temp))
Building prefix dict from the default dictionary ...
Loading model from cache C:\Users\Administrator\AppData\Local\Temp\jieba.cache
测试数据: 此外,自本周(6月12日)起,除小米手机6等15款机型外,其余机型已暂停更新发布(含开发版/体验版内测,稳定版暂不受影响),以确保工程师可以集中全部精力进行系统优化工作。有人猜测这也是将精力主要用到MIUI 9的研发之中。
MIUI 8去年5月发布,距今已有一年有余,也是时候更新换代了。
当然,关于MIUI 9的确切信息,我们还是等待官方消息。
Loading model cost 0.488 seconds.
Prefix dict has been built successfully.
测试分词结果: 此外 本周 除 小米 手机 款 机型 外 机型 暂停 更新 发布 含 开发 版 体验版 内测 稳定版 暂不受 影响 确保 工程师 集中 全部 精力 进行 系统优化 工作 有人 猜测 精力 主要 用到 MIUI9 研发 之中 MIUI8 去年 发布 距今已有 一年 有余 更新换代 当然 MIUI9 确切 信息 等待 官方消息
分词并保存分词结果到 corpus.pkl
# 假如路径存在,则直接读取,否则进行分词,并保存到本地
if os.path.exists('dataset/corpus.pkl'):
with open('dataset/corpus.pkl', 'rb') as file:
corpus = pickle.load(file)
else:
corpus = df['content'].map(split_text)
# 将处理好的分词保存到 corpus.pkl,方便下次调用
with open('dataset/corpus.pkl', 'wb') as file:
pickle.dump(corpus, file)
print(corpus.shape)
print(type(corpus[0]))
print(corpus[0])
print(len(set(corpus[0].split())))
(87052,)
<class 'str'>
此外 本周 除 小米 手机 款 机型 外 机型 暂停 更新 发布 含 开发 版 体验版 内测 稳定版 暂不受 影响 确保 工程师 集中 全部 精力 进行 系统优化 工作 有人 猜测 精力 主要 用到 MIUI9 研发 之中 MIUI8 去年 发布 距今已有 一年 有余 更新换代 当然 MIUI9 确切 信息 等待 官方消息
45
查看所有新闻对应的 source
df['source'].value_counts().head()
source
新华社 78661
中国新闻网 523
中国证券报?中证网 510
参考消息网 385
环球网 308
Name: count, dtype: int64
查看含有新华社的 source
df[df['source'].str.contains('新华')]['source'].value_counts()
source
新华社 78661
新华网 172
新华社新媒体专线 10
新华日报1 3
新华国际 2
新华社@ 1
新华通讯社 1
新华社@http://tech.163.com/17/0619/15/CNA7J2EJ00097U7T.html 1
新华社@http://tech.huanqiu.com/intelligent/2017-06/10868069.html?qq-pf-to=pcqq.c2c 1
新华网@ 1
新华网@http://tech.163.com/17/0620/15/CNCQ8UH500097U81.html 1
新华国际微博 1
Name: count, dtype: int64
认为上面类似‘新华社@’,‘新华社新媒体专线’等都属于新华社
进行 0 1 标记 source 为新华社的新闻和其他新闻
label = df['source'].copy()
target = label.str.contains('新华') * 1 # series.str.contains() 返回布尔索引,用*1转为 0 1
target[:5]
0 0
1 0
2 0
3 1
4 0
Name: source, dtype: int32
3、逆向文档率 TF-IDF 特征提取
corpus
0 此外 本周 除 小米 手机 款 机型 外 机型 暂停 更新 发布 含 开发 版 体验版 内测...
1 骁龙 835 唯一 Windows10 桌面 平台 认证 ARM 处理器 高通 强调 不会 ...
2 此前 一加 3T 搭载 3400mAh 电池 DashCharge 快充 规格 5V 4A ...
3 这是 18 葡萄牙 中部 佩德罗 冈 地区 拍摄 森林 大火 烧毁 汽车 新华社 记者 张立 云摄
4 原 标题 44 岁 女子 跑 深圳 约会 网友 拒 暴雨 裸身 奔走 … … @ 深圳 交警...
...
89606 新华社 照片 多伦多 2017 \ n 体育 冰球 国家 女子 冰球队 海外 选秀 多伦多 ...
89607 新华社 兰州 日电 王衡 徐丹 记者 甘肃省 交通运输 厅 获悉 甘肃 近日 集中 开建 高...
89608 \ n \ n2017 29 法国巴黎 郊外 凡尔赛宫 法国 总统 马克 龙 出席 新闻 发...
89609 \ n \ n2017 25 美国 马萨诸塞州 剑桥市 哈佛大学 毕业生 毕业典礼 欢呼 新...
89610 新华社 德国 杜塞尔多夫 6 6 日电 题 乒乓 女球迷 \ n 新华社 记者 王子江 张寒...
Name: content, Length: 87052, dtype: object
计算词频矩阵 TF
countvectorizer = CountVectorizer(encoding='gb18030',min_df=0.015)
countvector = countvectorizer.fit_transform(corpus) # 学习词频稀疏矩阵
print(countvector.shape)
print(countvector[:1]) # 查看第一个文档的词频向量(只显示非零元素及其索引)
(87052, 884)
(0, 590) 1
(0, 461) 1
(0, 271) 2
(0, 416) 1
(0, 432) 1
(0, 669) 1
(0, 860) 1
(0, 184) 1
(0, 822) 1
(0, 385) 1
(0, 103) 1
(0, 667) 1
(0, 263) 1
(0, 44) 1
(0, 174) 1
countvector_arr = countvector.toarray() # 转换稀疏矩阵为数组
print(countvector_arr.shape)
countvector_arr
(87052, 884)
array([[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
...,
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 6, 0]], dtype=int64)
将词频矩阵转为 TF-IDF 矩阵
tfidftransformer = TfidfTransformer()
# 拟合后返回tfidf稀疏矩阵
tfidf = tfidftransformer.fit_transform(countvector)
print(tfidf.shape)
print(tfidf[0])
(87052, 884)
(0, 44) 0.2622676107691569
(0, 103) 0.20106899736814415
(0, 174) 0.23459189038564443
(0, 184) 0.2468108139475762
(0, 263) 0.22918780918486897
(0, 271) 0.4315609902828978
(0, 385) 0.18500978673205756
(0, 416) 0.2635573764739399
(0, 432) 0.21737704053822574
(0, 461) 0.28783155518886216
(0, 590) 0.2377411770105167
(0, 667) 0.28309620126453316
(0, 669) 0.2671380200521529
(0, 822) 0.15081444348966608
(0, 860) 0.2674007239601568
拆分数据集(70%训练集,30%测试集)
# train_test_split(数据集,标签(source),测试集比例)
x_train,x_test,y_train,y_test = train_test_split(tfidf.toarray(),target,test_size=0.3)
x_train.shape,x_test.shape,y_train.shape,y_test.shape
((60936, 884), (26116, 884), (60936,), (26116,))
4、MultinomialNB 分类模型进行训练
初始化分类模型 MultinomialNB
estimator = MultinomialNB()
estimator
对于文本的特征(TF-IDF)和label(0-1是否为新华社)进行训练
estimator.fit(x_train,y_train)
评估模型
y_pred = estimator.predict(x_test)
print(classification_report(y_test,y_pred,labels=[0,1],target_names=['others','新华']))
precision recall f1-score support
others 0.45 0.65 0.54 2530
新华 0.96 0.92 0.94 23586
accuracy 0.89 26116
macro avg 0.71 0.78 0.74 26116
weighted avg 0.91 0.89 0.90 26116
模型的整体精确度达到了 0.89,说明模型在大多数情况下能够正确分类文本
5、基于模型进行风格预测并找出可能抄袭的文章
# compare_news_df中,prediction1代表预测文章的风格是新华社风格;labels1说明实际为新华社
prediction = estimator.predict(tfidf.toarray())
compare_news_df = pd.DataFrame({"prediction":prediction,'labels':target.values})
print(compare_news_df.head(2))
# 在实际非新华社的文章中找出预测为新华社风格的文章
copy_news_index = compare_news_df[(compare_news_df['labels'] == 0) & (compare_news_df['prediction'] == 1)]
print(copy_news_index.head(10))
print("模型预测结果与实际标签不符(即判定为抄袭嫌疑)的文章数:{}".format(len(copy_news_index)))
prediction labels
0 0 0
1 0 0
prediction labels
4 1 0
15 1 0
24 1 0
28 1 0
30 1 0
32 1 0
35 1 0
37 1 0
38 1 0
43 1 0
模型预测结果与实际标签不符(即判定为抄袭嫌疑)的文章数:2823
有 2814 则新闻预测 source 为新华社,但是 label 却不是,所以这些与新华社新闻风格相似的有抄袭嫌疑
可以看到,4 和 24 号文章都有抄袭嫌疑
# 实际为新华社和非新华社新闻的index
xinhuashe_news_index = compare_news_df[compare_news_df['labels'] == 1].index
no_xinhuashe_news_index = compare_news_df[compare_news_df['labels'] == 0].index
print(xinhuashe_news_index)
print(no_xinhuashe_news_index)
Index([ 3, 17, 25, 27, 39, 51, 65, 95, 102, 111,
...
87042, 87043, 87044, 87045, 87046, 87047, 87048, 87049, 87050, 87051],
dtype='int64', length=78855)
Index([ 0, 1, 2, 4, 5, 6, 7, 8, 9, 10,
...
8544, 8545, 8546, 8547, 8548, 8549, 8550, 8551, 8552, 8553],
dtype='int64', length=8197)
6、使用 K-Means 对文章进行聚类
"""
sklearn中的k-means算法只支持欧氏距离计算相似度
在文本的相似度比较中我们一般使用余弦距离。
欧氏距离衡量的是空间各点的绝对距离,跟各个点所在的位置坐标直接相关;
而余弦距离衡量的是空间向量的夹角,更加体现在方向上的差异,而不是位置。
"""
'\nsklearn中的k-means算法只支持欧氏距离计算相似度\n在文本的相似度比较中我们一般使用余弦距离。\n\n欧氏距离衡量的是空间各点的绝对距离,跟各个点所在的位置坐标直接相关;\n而余弦距离衡量的是空间向量的夹角,更加体现在方向上的差异,而不是位置。\n'
对全量文本的 TF-IDF 进行范式归一化
# 对特征向量进行L2范数归一化后,欧氏距离和余弦距离等价,便于进行k-means聚类
transfer = Normalizer()
scaled_array = transfer.fit_transform(tfidf.toarray())
对全量文本进行 K-Means 聚类
# 使用K-Means进行聚类降维,嫌疑新闻只和相同堆中的新华社新闻进行计算
estimator2 = KMeans(n_clusters=25)
estimator2.fit(scaled_array)
k_labels = estimator2.predict(scaled_array)
建立聚类类别与实为新华社文章的 index 之间的映射关系
k_labels_sr = pd.Series(k_labels)
class_id = defaultdict(set)
for index,value in k_labels_sr.items():
# 对真实为新华社的新闻index分组
if index in xinhuashe_news_index:
class_id[value].add(index)
class_id.keys()
dict_keys([13, 12, 6, 10, 24, 11, 2, 7, 15, 17, 20, 21, 4, 18, 22, 1, 16, 23, 5, 0, 14, 19, 8, 3, 9])
将聚类结果保存在 label.pkl 中
# 假如路径存在,则直接读取,否则进行分词,并保存到本地
import os
if os.path.exists('dataset/label.pkl'):
with open('dataset/label.pkl', 'rb') as file:
label = pickle.load(file)
else:
label = df['content'].map(class_id)
# 将处理好的分词保存到 label.pkl,方便下次调用
with open('dataset/label.pkl', 'wb') as file:
pickle.dump(label, file)
7、查看相似文章(测试结果)
def find_similar_text(copy_index,top=10):
# 只在新华社发布的文章中查找
label = k_labels_sr[copy_index]
dist_dict = {i:cosine_similarity(tfidf[copy_index],tfidf[i]) for i in class_id[label]}
# 从大到小排序
return sorted(dist_dict.items(),key=lambda x:x[1][0], reverse=True)[:top]
copy_index = 3352
抄袭嫌疑新闻与相似 Top10 新华社新闻
similar_list = find_similar_text(copy_index)
ic(len(similar_list))
similar_list
ic| len(similar_list): 10
[(3134, array([[0.96848961]])),
(63509, array([[0.94642972]])),
(29439, array([[0.94283134]])),
(3218, array([[0.87621302]])),
(980, array([[0.87534597]])),
(29613, array([[0.86935867]])),
(29886, array([[0.8621539]])),
(64044, array([[0.85277574]])),
(29775, array([[0.84875245]])),
(64756, array([[0.73394623]]))]
查看抄袭新闻与新华社原文
print('抄袭嫌疑新闻内容:\n',df.iloc[copy_index].content)
抄袭嫌疑新闻内容:
中国5月份56座城市新建商品住宅价格环比上涨,4月份为58座上涨。5月份15个一线和热点二线城市房地产市场基本稳定,5月份房地产调控政策效果继续显现。
统计局:15个一线和热点二线城市房价同比涨幅全部回落
国家统计局城市司高级统计师刘建伟解读5月份房价数据
5月份一二线城市房价平均涨幅继续回落
国家统计局今日发布了2017年5月份70个大中城市住宅销售价格统计数据。对此,国家统计局城市司高级统计师刘建伟进行了解读。
一、15个一线和热点二线城市新建商品住宅价格同比涨幅全部回落、9个城市环比下降或持平
5月份,因地制宜、因城施策的房地产调控政策效果继续显现,15个一线和热点二线城市房地产市场基本稳定。从同比看,15个城市新建商品住宅价格涨幅均比上月回落,回落幅度在0.5至6.4个百分点之间。从环比看,9个城市新建商品住宅价格下降或持平;5个城市涨幅在0.5%以内。
二、70个大中城市中一二线城市房价同比涨幅持续回落
5月份,70个城市中新建商品住宅和二手住宅价格同比涨幅比上月回落的城市分别有29和18个。其中,一二线城市同比涨幅回落尤其明显。据测算,一线城市新建商品住宅和二手住宅价格同比涨幅均连续8个月回落,5月份比4月份分别回落2.2和1.7个百分点;二线城市新建商品住宅和二手住宅价格同比涨幅分别连续6个月和4个月回落,5月份比4月份分别回落0.8和0.5个百分点。
三、70个大中城市中房价环比下降及涨幅回落城市个数均有所增加
5月份,70个城市中新建商品住宅价格环比下降的城市有9个,比上月增加1个;涨幅回落的城市有26个,比上月增加3个。二手住宅价格环比下降的城市有7个,比上月增加2个;涨幅回落的城市有30个,比上月增加8个。
# 找一篇相似的新华社原文
similar2 = similar_list[0][0]
print('新华社相似原文:\n',df.iloc[similar2].content)
新华社相似原文:
国家统计局19日发布数据,5月份,15个一线和热点二线城市新建商品住宅价格同比涨幅全部回落,其中9个城市环比下降或持平。这9个价格环比下降或持平的城市为:北京、上海、南京、杭州、合肥、福州、郑州、深圳、成都。
“5月份,因地制宜、因城施策的房地产调控政策效果继续显现,15个一线和热点二线城市房地产市场基本稳定。”国家统计局城市司高级统计师刘建伟说,从同比看,15个城市新建商品住宅价格涨幅均比上月回落,回落幅度在0.5至6.4个百分点之间。从环比看,9个城市新建商品住宅价格下降或持平;5个城市涨幅在0.5%以内。
国家统计局当天还发布了5月份70个大中城市住宅销售价格统计数据。刘建伟介绍,5月份,70个大中城市中新建商品住宅和二手住宅价格同比涨幅比上月回落的城市分别有29和18个。其中,一二线城市同比涨幅回落尤其明显。据测算,一线城市新建商品住宅和二手住宅价格同比涨幅均连续8个月回落,5月份比4月份分别回落2.2和1.7个百分点;二线城市新建商品住宅和二手住宅价格同比涨幅分别连续6个月和4个月回落,5月份比4月份分别回落0.8和0.5个百分点。
此外,70个大中城市中房价环比下降及涨幅回落城市个数均有所增加。统计显示,5月份,70个大中城市中新建商品住宅价格环比下降的城市有9个,比上月增加1个;涨幅回落的城市有26个,比上月增加3个。二手住宅价格环比下降的城市有7个,比上月增加2个;涨幅回落的城市有30个,比上月增加8个。
# 找一篇相似的新华社原文
similar3 = similar_list[1][0]
print('新华社相似原文:\n',df.iloc[similar3].content)
新华社相似原文:
新华社北京5月18日电国家统计局18日发布的数据显示,4月份,15个一线和热点二线城市新建商品住宅价格同比涨幅回落,9个城市环比下降或持平。\n 这9个价格环比下降或持平的城市是天津、上海、南京、合肥、福州、杭州、厦门、深圳、成都。\n “4月份,因地制宜、因城施策的房地产调控政策继续发挥作用。”国家统计局城市司高级统计师刘建伟说,从同比看,15个城市新建商品住宅价格涨幅均比上月回落,回落幅度在0.7至7.4个百分点之间。从环比看,9个城市新建商品住宅价格下降或持平;5个城市涨幅回落,回落幅度在0.2至1.1个百分点之间。\n 国家统计局当天还发布了4月份70个大中城市住宅销售价格统计数据。刘建伟介绍,从同比看,4月份70个城市中有30个城市新建商品住宅价格涨幅比上月回落,比3月份增加6个,回落城市中23个为一二线城市。初步测算,一线城市新建商品住宅价格同比涨幅连续7个月回落,4月份比3月份回落2.8个百分点;二线城市新建商品住宅价格同比涨幅连续5个月回落,4月份比3月份回落1.0个百分点;三线城市新建商品住宅价格同比涨幅略有扩大,4月份比3月份扩大0.4个百分点。\n 从环比看,4月份70个城市中有23个城市新建商品住宅价格涨幅比上月回落,比3月份增加13个;7个城市由上月上涨转为持平或下降;3个城市降幅扩大。(完)
8、基于编辑距离进行文章对比
嫌疑文章和新华社原文整体编辑距离
editdistance.eval(df.iloc[copy_index].content, df.iloc[similar2].content)
299
import editdistance
from IPython.display import display, HTML
# 示例疑似文章和原文
suspected_article = df.iloc[copy_index].content
original_article = df.iloc[similar2].content
# 分句函数
def split_into_sentences(text):
return text.split('。')
# 对齐句子
def align_sentences(sentences1, sentences2):
max_len = max(len(sentences1), len(sentences2))
sentences1 += [''] * (max_len - len(sentences1))
sentences2 += [''] * (max_len - len(sentences2))
return sentences1, sentences2
# 分句
suspected_sentences = split_into_sentences(suspected_article)
original_sentences = split_into_sentences(original_article)
# 对齐句子
aligned_suspected, aligned_original = align_sentences(suspected_sentences, original_sentences)
# 设置编辑距离阈值
threshold = 100
# 逐句比对并显示彩色文本
html_output = ""
for suspected_sentence, original_sentence in zip(aligned_suspected, aligned_original):
distance = editdistance.eval(suspected_sentence, original_sentence)
if distance <= threshold:
html_output += f"<p><span style='color:red;'>{suspected_sentence}</span> vs <span style='color:red;'>{original_sentence}</span> (编辑距离为:{distance})</p>"
else:
html_output += f"<p>{suspected_sentence} vs {original_sentence} (编辑距离为:{distance})</p>"
display(HTML(html_output))
中国5月份56座城市新建商品住宅价格环比上涨,4月份为58座上涨 vs 国家统计局19日发布数据,5月份,15个一线和热点二线城市新建商品住宅价格同比涨幅全部回落,其中9个城市环比下降或持平 (编辑距离为:43)
5月份15个一线和热点二线城市房地产市场基本稳定,5月份房地产调控政策效果继续显现 vs 这9个价格环比下降或持平的城市为:北京、上海、南京、杭州、合肥、福州、郑州、深圳、成都 (编辑距离为:41)
统计局:15 个一线和热点二线城市房价同比涨幅全部回落
国家统计局城市司高级统计师刘建伟解读 5 月份房价数据
5 月份一二线城市房价平均涨幅继续回落
国家统计局今日发布了 2017 年 5 月份 70 个大中城市住宅销售价格统计数据 vs
“5 月份,因地制宜、因城施策的房地产调控政策效果继续显现,15 个一线和热点二线城市房地产市场基本稳定 (编辑距离为:108)
对此,国家统计局城市司高级统计师刘建伟进行了解读 vs ”国家统计局城市司高级统计师刘建伟说,从同比看,15 个城市新建商品住宅价格涨幅均比上月回落,回落幅度在 0.5 至 6.4 个百分点之间 (编辑距离为:50)
一、15 个一线和热点二线城市新建商品住宅价格同比涨幅全部回落、9 个城市环比下降或持平
5 月份,因地制宜、因城施策的房地产调控政策效果继续显现,15 个一线和热点二线城市房地产市场基本稳定 vs 从环比看,9 个城市新建商品住宅价格下降或持平;5 个城市涨幅在 0.5%以内 (编辑距离为:79)
从同比看,15 个城市新建商品住宅价格涨幅均比上月回落,回落幅度在 0.5 至 6.4 个百分点之间 vs
国家统计局当天还发布了 5 月份 70 个大中城市住宅销售价格统计数据 (编辑距离为:44)
从环比看,9 个城市新建商品住宅价格下降或持平;5 个城市涨幅在 0.5%以内 vs 刘建伟介绍,5 月份,70 个大中城市中新建商品住宅和二手住宅价格同比涨幅比上月回落的城市分别有 29 和 18 个 (编辑距离为:38)
二、70 个大中城市中一二线城市房价同比涨幅持续回落
5 月份,70 个城市中新建商品住宅和二手住宅价格同比涨幅比上月回落的城市分别有 29 和 18 个 vs 其中,一二线城市同比涨幅回落尤其明显 (编辑距离为:65)
其中,一二线城市同比涨幅回落尤其明显 vs 据测算,一线城市新建商品住宅和二手住宅价格同比涨幅均连续 8 个月回落,5 月份比 4 月份分别回落 2.2 和 1.7 个百分点;二线城市新建商品住宅和二手住宅价格同比涨幅分别连续 6 个月和 4 个月回落,5 月份比 4 月份分别回落 0.8 和 0.5 个百分点 (编辑距离为:102)
据测算,一线城市新建商品住宅和二手住宅价格同比涨幅均连续 8 个月回落,5 月份比 4 月份分别回落 2.2 和 1.7 个百分点;二线城市新建商品住宅和二手住宅价格同比涨幅分别连续 6 个月和 4 个月回落,5 月份比 4 月份分别回落 0.8 和 0.5 个百分点 vs
此外,70 个大中城市中房价环比下降及涨幅回落城市个数均有所增加 (编辑距离为:106)
三、70 个大中城市中房价环比下降及涨幅回落城市个数均有所增加
5 月份,70 个城市中新建商品住宅价格环比下降的城市有 9 个,比上月增加 1 个;涨幅回落的城市有 26 个,比上月增加 3 个 vs 统计显示,5 月份,70 个大中城市中新建商品住宅价格环比下降的城市有 9 个,比上月增加 1 个;涨幅回落的城市有 26 个,比上月增加 3 个 (编辑距离为:40)
二手住宅价格环比下降的城市有 7 个,比上月增加 2 个;涨幅回落的城市有 30 个,比上月增加 8 个 vs 二手住宅价格环比下降的城市有 7 个,比上月增加 2 个;涨幅回落的城市有 30 个,比上月增加 8 个 (编辑距离为:0)
vs
(编辑距离为:0)
import editdistance
from IPython.display import display, HTML
# 示例疑似文章和原文
suspected_article = df.iloc[copy_index].content
original_article = df.iloc[similar2].content
# 分句函数
def split_into_sentences(text):
return text.split('。')
# 对齐句子
def align_sentences(sentences1, sentences2):
max_len = max(len(sentences1), len(sentences2))
sentences1 += [''] * (max_len - len(sentences1))
sentences2 += [''] * (max_len - len(sentences2))
return sentences1, sentences2
# 分句
suspected_sentences = split_into_sentences(suspected_article)
original_sentences = split_into_sentences(original_article)
# 对齐句子
aligned_suspected, aligned_original = align_sentences(suspected_sentences, original_sentences)
# 设置编辑距离阈值
threshold = 50
# 创建用于颜色标注的HTML标记
def create_colored_sentence(sentence, color='red'):
return f"<span style='color:{color};'>{sentence}</span>"
# 初始化标记后的句子列表
marked_original_sentences = []
marked_suspected_sentences = []
# 逐句比对并在原文和疑似抄袭文章中进行颜色标注
for i, (suspected_sentence, original_sentence) in enumerate(zip(aligned_suspected, aligned_original)):
distance = editdistance.eval(suspected_sentence, original_sentence)
if distance <= threshold:
marked_sentence_original = create_colored_sentence(original_sentences[i])
marked_sentence_suspected = create_colored_sentence(suspected_sentences[i])
else:
marked_sentence_original = original_sentences[i]
marked_sentence_suspected = suspected_sentences[i]
marked_original_sentences.append(marked_sentence_original)
marked_suspected_sentences.append(marked_sentence_suspected)
# 将标记后的句子重新组合成原文和疑似抄袭文章
marked_original_article = '。'.join(marked_original_sentences).strip()
marked_suspected_article = '。'.join(marked_suspected_sentences).strip()
# 显示颜色标注后的原文和疑似抄袭文章
print("原文:")
display(HTML(marked_original_article))
print("\n疑似抄袭文章:")
display(HTML(marked_suspected_article))
原文:
国家统计局 19 日发布数据,5 月份,15 个一线和热点二线城市新建商品住宅价格同比涨幅全部回落,其中 9 个城市环比下降或持平。这 9 个价格环比下降或持平的城市为:北京、上海、南京、杭州、合肥、福州、郑州、深圳、成都。
“5 月份,因地制宜、因城施策的房地产调控政策效果继续显现,15 个一线和热点二线城市房地产市场基本稳定。”国家统计局城市司高级统计师刘建伟说,从同比看,15 个城市新建商品住宅价格涨幅均比上月回落,回落幅度在 0.5 至 6.4 个百分点之间。从环比看,9 个城市新建商品住宅价格下降或持平;5 个城市涨幅在 0.5%以内。
国家统计局当天还发布了 5 月份 70 个大中城市住宅销售价格统计数据。刘建伟介绍,5 月份,70 个大中城市中新建商品住宅和二手住宅价格同比涨幅比上月回落的城市分别有 29 和 18 个。其中,一二线城市同比涨幅回落尤其明显。据测算,一线城市新建商品住宅和二手住宅价格同比涨幅均连续 8 个月回落,5 月份比 4 月份分别回落 2.2 和 1.7 个百分点;二线城市新建商品住宅和二手住宅价格同比涨幅分别连续 6 个月和 4 个月回落,5 月份比 4 月份分别回落 0.8 和 0.5 个百分点。
此外,70 个大中城市中房价环比下降及涨幅回落城市个数均有所增加。统计显示,5 月份,70 个大中城市中新建商品住宅价格环比下降的城市有 9 个,比上月增加 1 个;涨幅回落的城市有 26 个,比上月增加 3 个。二手住宅价格环比下降的城市有 7 个,比上月增加 2 个;涨幅回落的城市有 30 个,比上月增加 8 个。
疑似抄袭文章:
中国 5 月份 56 座城市新建商品住宅价格环比上涨,4 月份为 58 座上涨。5 月份 15 个一线和热点二线城市房地产市场基本稳定,5 月份房地产调控政策效果继续显现。
统计局:15 个一线和热点二线城市房价同比涨幅全部回落
国家统计局城市司高级统计师刘建伟解读 5 月份房价数据
5 月份一二线城市房价平均涨幅继续回落
国家统计局今日发布了 2017 年 5 月份 70 个大中城市住宅销售价格统计数据。对此,国家统计局城市司高级统计师刘建伟进行了解读。
一、15 个一线和热点二线城市新建商品住宅价格同比涨幅全部回落、9 个城市环比下降或持平
5 月份,因地制宜、因城施策的房地产调控政策效果继续显现,15 个一线和热点二线城市房地产市场基本稳定。从同比看,15 个城市新建商品住宅价格涨幅均比上月回落,回落幅度在 0.5 至 6.4 个百分点之间。从环比看,9 个城市新建商品住宅价格下降或持平;5 个城市涨幅在 0.5%以内。
二、70 个大中城市中一二线城市房价同比涨幅持续回落
5 月份,70 个城市中新建商品住宅和二手住宅价格同比涨幅比上月回落的城市分别有 29 和 18 个。其中,一二线城市同比涨幅回落尤其明显。据测算,一线城市新建商品住宅和二手住宅价格同比涨幅均连续 8 个月回落,5 月份比 4 月份分别回落 2.2 和 1.7 个百分点;二线城市新建商品住宅和二手住宅价格同比涨幅分别连续 6 个月和 4 个月回落,5 月份比 4 月份分别回落 0.8 和 0.5 个百分点。
三、70 个大中城市中房价环比下降及涨幅回落城市个数均有所增加
5 月份,70 个城市中新建商品住宅价格环比下降的城市有 9 个,比上月增加 1 个;涨幅回落的城市有 26 个,比上月增加 3 个。二手住宅价格环比下降的城市有 7 个,比上月增加 2 个;涨幅回落的城市有 30 个,比上月增加 8 个。
参考文献
[1] https://www.cnblogs.com/zhaop8078/p/13676158.html
[2] https://blog.csdn.net/qq_36667170/article/details/121656279
附:完整代码
import pickle, os
import jieba
import pandas as pd
from icecream import ic
from sklearn.cluster import KMeans
from collections import defaultdict
from sklearn.preprocessing import Normalizer # 把tfidf归一化为单位向量
from sklearn.naive_bayes import MultinomialNB # 多项式贝叶斯模型
from sklearn.model_selection import train_test_split # 拆分数据集
from sklearn.feature_extraction.text import CountVectorizer, TfidfTransformer # 统计词频、计算tfidf
from sklearn.metrics import classification_report # 分类报告 评估模型
from sklearn.metrics.pairwise import cosine_similarity # 计算余弦相似度
import editdistance # 计算编辑距离
with open('dataset/chinese_stopwords.txt','r',encoding='utf-8') as file:
stopwords = [line.strip() for line in file]
stopwords[:10]
# gb18030编码收录的中文字符更全,对中文文档兼容性更好
df = pd.read_csv('dataset/sqlResult.csv',encoding='gb18030')
df.head(2)
df.info()
# 去除content列存在缺失值的行
df.dropna(subset=['content'],inplace=True)
# 去除
df.dropna(subset=['source'],inplace=True)
df.info()
def split_text(text):
text = text.replace(' ', '').replace('\n','').replace('\r','')
text2 = jieba.cut(text)
# 去掉停用词
result = ' '.join([w for w in text2 if w not in stopwords])
return result
temp = df.iloc[0].content
print('测试数据:',temp)
print('测试分词结果:',split_text(temp))
# 假如路径存在,则直接读取,否则进行分词,并保存到本地
if os.path.exists('dataset/corpus.pkl'):
with open('dataset/corpus.pkl', 'rb') as file:
corpus = pickle.load(file)
else:
corpus = df['content'].map(split_text)
# 将处理好的分词保存到 corpus.pkl,方便下次调用
with open('dataset/corpus.pkl', 'wb') as file:
pickle.dump(corpus, file)
print(corpus.shape)
print(type(corpus[0]))
print(corpus[0])
print(len(set(corpus[0].split())))
df['source'].value_counts().head()
df[df['source'].str.contains('新华')]['source'].value_counts()
label = df['source'].copy()
target = label.str.contains('新华') * 1 # series.str.contains() 返回布尔索引,用*1转为 0 1
target[:5]
corpus
countvectorizer = CountVectorizer(encoding='gb18030',min_df=0.015)
countvector = countvectorizer.fit_transform(corpus) # 学习词频稀疏矩阵
print(countvector.shape)
print(countvector[:1]) # 查看第一个文档的词频向量(只显示非零元素及其索引)
countvector_arr = countvector.toarray() # 转换稀疏矩阵为数组
print(countvector_arr.shape)
countvector_arr
tfidftransformer = TfidfTransformer()
# 拟合后返回tfidf稀疏矩阵
tfidf = tfidftransformer.fit_transform(countvector)
print(tfidf.shape)
print(tfidf[0])
# train_test_split(数据集,标签(source),测试集比例)
x_train,x_test,y_train,y_test = train_test_split(tfidf.toarray(),target,test_size=0.3)
x_train.shape,x_test.shape,y_train.shape,y_test.shape
estimator = MultinomialNB()
estimator
estimator.fit(x_train,y_train)
y_pred = estimator.predict(x_test)
print(classification_report(y_test,y_pred,labels=[0,1],target_names=['others','新华']))
# compare_news_df中,prediction1代表预测文章的风格是新华社风格;labels1说明实际为新华社
prediction = estimator.predict(tfidf.toarray())
compare_news_df = pd.DataFrame({"prediction":prediction,'labels':target.values})
print(compare_news_df.head(2))
# 在实际非新华社的文章中找出预测为新华社风格的文章
copy_news_index = compare_news_df[(compare_news_df['labels'] == 0) & (compare_news_df['prediction'] == 1)]
print(copy_news_index.head(10))
print("模型预测结果与实际标签不符(即判定为抄袭嫌疑)的文章数:{}".format(len(copy_news_index)))
# 实际为新华社和非新华社新闻的index
xinhuashe_news_index = compare_news_df[compare_news_df['labels'] == 1].index
no_xinhuashe_news_index = compare_news_df[compare_news_df['labels'] == 0].index
print(xinhuashe_news_index)
print(no_xinhuashe_news_index)
"""
sklearn中的k-means算法只支持欧氏距离计算相似度
在文本的相似度比较中我们一般使用余弦距离。
欧氏距离衡量的是空间各点的绝对距离,跟各个点所在的位置坐标直接相关;
而余弦距离衡量的是空间向量的夹角,更加体现在方向上的差异,而不是位置。
"""
# 对特征向量进行L2范数归一化后,欧氏距离和余弦距离等价,便于进行k-means聚类
transfer = Normalizer()
scaled_array = transfer.fit_transform(tfidf.toarray())
# 使用K-Means进行聚类降维,嫌疑新闻只和相同堆中的新华社新闻进行计算
estimator2 = KMeans(n_clusters=25)
estimator2.fit(scaled_array)
k_labels = estimator2.predict(scaled_array)
k_labels_sr = pd.Series(k_labels)
class_id = defaultdict(set)
for index,value in k_labels_sr.items():
# 对真实为新华社的新闻index分组
if index in xinhuashe_news_index:
class_id[value].add(index)
class_id.keys()
# 假如路径存在,则直接读取,否则进行分词,并保存到本地
import os
if os.path.exists('dataset/label.pkl'):
with open('dataset/label.pkl', 'rb') as file:
label = pickle.load(file)
else:
label = df['content'].map(class_id)
# 将处理好的分词保存到 label.pkl,方便下次调用
with open('dataset/label.pkl', 'wb') as file:
pickle.dump(label, file)
def find_similar_text(copy_index,top=10):
# 只在新华社发布的文章中查找
label = k_labels_sr[copy_index]
dist_dict = {i:cosine_similarity(tfidf[copy_index],tfidf[i]) for i in class_id[label]}
# 从大到小排序
return sorted(dist_dict.items(),key=lambda x:x[1][0], reverse=True)[:top]
copy_index = 3352
similar_list = find_similar_text(copy_index)
ic(len(similar_list))
similar_list
print('抄袭嫌疑新闻内容:\n',df.iloc[copy_index].content)
# 找一篇相似的新华社原文
similar2 = similar_list[0][0]
print('新华社相似原文:\n',df.iloc[similar2].content)
# 找一篇相似的新华社原文
similar3 = similar_list[1][0]
print('新华社相似原文:\n',df.iloc[similar3].content)
editdistance.eval(df.iloc[copy_index].content, df.iloc[similar2].content)
import editdistance
from IPython.display import display, HTML
# 示例疑似文章和原文
suspected_article = df.iloc[copy_index].content
original_article = df.iloc[similar2].content
# 分句函数
def split_into_sentences(text):
return text.split('。')
# 对齐句子
def align_sentences(sentences1, sentences2):
max_len = max(len(sentences1), len(sentences2))
sentences1 += [''] * (max_len - len(sentences1))
sentences2 += [''] * (max_len - len(sentences2))
return sentences1, sentences2
# 分句
suspected_sentences = split_into_sentences(suspected_article)
original_sentences = split_into_sentences(original_article)
# 对齐句子
aligned_suspected, aligned_original = align_sentences(suspected_sentences, original_sentences)
# 设置编辑距离阈值
threshold = 100
# 逐句比对并显示彩色文本
html_output = ""
for suspected_sentence, original_sentence in zip(aligned_suspected, aligned_original):
distance = editdistance.eval(suspected_sentence, original_sentence)
if distance <= threshold:
html_output += f"<p><span style='color:red;'>{suspected_sentence}</span> vs <span style='color:red;'>{original_sentence}</span> (编辑距离为:{distance})</p>"
else:
html_output += f"<p>{suspected_sentence} vs {original_sentence} (编辑距离为:{distance})</p>"
display(HTML(html_output))
import editdistance
from IPython.display import display, HTML
# 示例疑似文章和原文
suspected_article = df.iloc[copy_index].content
original_article = df.iloc[similar2].content
# 分句函数
def split_into_sentences(text):
return text.split('。')
# 对齐句子
def align_sentences(sentences1, sentences2):
max_len = max(len(sentences1), len(sentences2))
sentences1 += [''] * (max_len - len(sentences1))
sentences2 += [''] * (max_len - len(sentences2))
return sentences1, sentences2
# 分句
suspected_sentences = split_into_sentences(suspected_article)
original_sentences = split_into_sentences(original_article)
# 对齐句子
aligned_suspected, aligned_original = align_sentences(suspected_sentences, original_sentences)
# 设置编辑距离阈值
threshold = 50
# 创建用于颜色标注的HTML标记
def create_colored_sentence(sentence, color='red'):
return f"<span style='color:{color};'>{sentence}</span>"
# 初始化标记后的句子列表
marked_original_sentences = []
marked_suspected_sentences = []
# 逐句比对并在原文和疑似抄袭文章中进行颜色标注
for i, (suspected_sentence, original_sentence) in enumerate(zip(aligned_suspected, aligned_original)):
distance = editdistance.eval(suspected_sentence, original_sentence)
if distance <= threshold:
marked_sentence_original = create_colored_sentence(original_sentences[i])
marked_sentence_suspected = create_colored_sentence(suspected_sentences[i])
else:
marked_sentence_original = original_sentences[i]
marked_sentence_suspected = suspected_sentences[i]
marked_original_sentences.append(marked_sentence_original)
marked_suspected_sentences.append(marked_sentence_suspected)
# 将标记后的句子重新组合成原文和疑似抄袭文章
marked_original_article = '。'.join(marked_original_sentences).strip()
marked_suspected_article = '。'.join(marked_suspected_sentences).strip()
# 显示颜色标注后的原文和疑似抄袭文章
print("原文:")
display(HTML(marked_original_article))
print("\n疑似抄袭文章:")
display(HTML(marked_suspected_article))
标签:index,抄袭,回落,df,自动检测,城市,sentences,print,文本
From: https://www.cnblogs.com/sherioc/p/18253878