该问题可归类为监督性学习的分类问题。解决该问题的方法有:
-
普通的概率模型:如朴素贝叶斯,该模型最主要的缺点是无法捕捉词与词之间的上下文关系,特征与特征之间组合关系,因为该模型假设词与词之间是相互独立。
-
树型模型,如随机森林,提升树(xgboost,gdbt).该模型虽然很好地解决特征组合问题,但是对稀疏矩阵的效果不好,
特别是对针对当下的文本分类问题,因为one-hot处理后,形成巨大稀疏矩阵,几乎不可分割。 -
词向量化,比如word2vec,该模型是把词 word embedding 化,这样主要优点是很好捕捉上下关系。
#基本思想是根据上下文预测中间词(cbow), 或者中间词预测上下文(skip-gram,基于词思想提出用于分类模型的fast-text model,该模与word2vec区别是预测的是分类 -
深度学习,比如lstm,相比rnn,该模型能防止梯度弥散,同时能捕捉更长的上下文关系。因为权重共享,很难判断那个词对最终结果影响最明显。我们可加入attention层。深度学习虽然能很好拟好数据分布情况,但是对设备要求高。数据量大,网络层深,网络复杂等,这些都需要好的显卡支撑。当然为了提高运行速度,可充分利用预训练数据,比如bert
因此这里我们选用fast-text model,设备要求低,训练快,很好捕捉上下文关系,准确率高。
import json
import pandas as pd
import matplotlib.pyplot as plt
import random
import os
import fasttext
import numpy as np
import json
def loadData()
'''
导入数据
'''
data = []
with open("nlp-problem//News_Category_Dataset_v2.json",'r',encoding='utf8') as f:
for line in f.readlines():
d = json.loads(line)
data.append(d)
def count_catagory(data):
'''
统计每个类别的数据
'''
nums_catagory= {}
for i in range(len(data)):
if data[i]['category'] in nums_catagory:
nums_catagory[data[i]['category']] +=1
else:
nums_catagory[data[i]['category']] =nums_catagory.setdefault(data[i]['category'],0)+1
return nums_catagory
def SegmentationCombine(data):
'''
1.分词,这里直接把每个单词独立切分成一个词,因为fast-text会自动捕获单词与单词之间关系,
不需要准确地切分成词组或者短语
2.大写转为小写
3.把标签值转 __label__ENTERTAINMENT 形式
'''
SegData=[]
for num in range(len(data)):
words = [word for word in re.sub("[^a-zA-Z]"," ",data[num]['headline'].lower()).split(' ') if len(word) >1]
SegData.append('__'+'label'+'__'+data[num]['category']+' '+','+' '+' '.join(words))
return SegData
def WriteTxt(prename,train_data):
'''
保存已经加工好的数据
'''
data_file = 'nlp-problem/{}_class.txt'.format(str(prename))
with open(data_file, 'w', encoding='utf-8') as data_f:
for values in train_data:
data_f.write(values)
data_f.write('\n')
def train_model(ipt=None, opt=None, model='', dim=dim, epoch=epoch, lr=lr, loss='softmax'):
'''
训练模型
ipt:文件路径
opt:保存路径
model:name
dim:投影层的维度
epoch:训练轮数
lr:学习率
loss:损失函数
'''
np.set_printoptions(suppress=True)
if os.path.isfile(model):
classifier = fasttext.load_model(model)
else:
classifier = fasttext.train_supervised(ipt, label='__label__', dim=dim, epoch=epoch,
lr=lr, wordNgrams=2, loss=loss)
classifier.save_model(opt)
return classifier
def CalPrecision(file):
'''
计算准确率,召回率
输入test文件路径
'''
total,recall,precision={},{},{}
with open(file,encoding='utf-8') as f:
for line in f:
label, content = line.split(',', 1)
total[label.strip().strip('')] = total.setdefault(label.strip().strip(),0)+1
PredictedLabels = classifier.predict(content.strip('\n'))[0][0]
recall[PredictedLabels] = recall.setdefault(PredictedLabels,0)+1
if label.strip() == PredictedLabels.strip():
precision[label.strip()]=precision.setdefault(label.strip(),0)+1
for k,_ in precision.items():
pre = precision[k] / (total.get(k,precision[k])+0.0001)
rec = precision[k] / (recall.get(k,precision[k])+0.0001)
F1 = (2 * pre * rec) / ((pre + rec)+0.0001)
print("{} : precision: {} recall: {} F1: {}".format(k.strip('__label__'),pre,rec,F1))
def predict(content,k):
'''
content:预测文本
k:输出的topics数
'''
words = [word for word in re.sub("[^a-zA-Z]"," ",content.lower()).split(' ') if len(word) >1]
pred = classifier.predict(' '.join(words),k)
return pred
if __name__ ='__main__':
proportion = 0.9
dim = 100
lr = 5
epoch = 5
data =loadData() # 导入数据
WriteTxt('train',trainClass) # 保存训练数据
WriteTxt('test',testClass) #保存测试数据
model = 'F://VS_python//nlp-problem//fasttext.model'
Train_file = 'F://VS_python//nlp-problem//train_class.txt'
Test_file='F://VS_python//nlp-problem//test_class.txt'
classifier = train_model(ipt=Train_file,
opt=model,
model=model,
dim=dim, epoch=epoch, lr=0.5
)
#test:
# content='senate blocks government funding bill tied to abortion'
# predict(content,5)
CalPrecision(Test_file)
content='senate blocks government funding bill tied to abortion'
predict(content,5)