因为这段时间正好在和朋友准备比赛,赛题大概是基于交通事故的事故描述,进行文本多分类。实现将数据库中已有的原因分类不明确的事故进行重分类,修复数据。也为未来交警判责提供参考信息。
一直以来都没有真正做过nlp方向的建模,借此机会也把比赛过程记录一下,一方面分享给有需要的朋友,另一方面也可以在以后能够方便复习。也希望各位大佬可以指出不好的地方,让小弟也能得到提高。
上图是一个交通事故原因的分类代码值与所对应的描述。一共有几百类,如果要建模训练出可以分几百类的模型,已目前所拥有的样本量来看,远远不够,因此我们希望能够人工or自动的将这些类别归纳为某k类,但具体k为几呢,这是个不容易解决的问题。
也因为这个问题,本文诞生了。
所谓聚类,是将物理或抽象对象的集合分成由类似的对象组成的多个类的过程被称为聚类。由聚类所生成的簇是一组数据对象的集合,这些对象与同一个簇中的对象彼此相似,与其他簇中的对象相异。
相比看到这,加上标题,大家就知道本文的主题思路是个什么样子了。
对,就是
1、先将所有的分类描述文本内容,利用jieba分词(附加上内部统计的交通类词库)分词、并去掉停用词(构建停用词库)
2、扔进word2vec模型中训练,得到一个向量词袋。
3、再将所有的文本内容,用一个n维向量来代替
4、丢进k-means中,循环k=1至10,绘出手肘图,找到最佳k值
5、丢入所有文本内容预测,给每一个类别增加一个新的类别值
根据上面的步骤,第一步,我们是对文本内容进行分词:
#导包
import numpy as np
import pandas as pd
import sys
from gensim.models import word2vec
import os
import gensim
from gensim.models.word2vec import LineSentence
导入需要用到的包
#读数据
data = pd.read_csv('./reason.csv')
data
可以看到数据(已经用excel将columns名修改为i和x):
#分词
import jieba
jieba.load_userdict('./dic.txt')
stop = [line.strip() for line in open('stopwords.txt').readlines()]
导入jieba库,导入我们的交通词库(交通词库是利用我们手中的描述数据进行计算词频后导出top30%)
#去停用词
out = ''
for index in range(len(data)):
ct = jieba.cut(data.loc[index,'x'])
out = ''
for word in ct:
if word not in stop:
out += word
out += " "
data.loc[index,'split'] = out
先去掉停用词(利用停用词词典)
然后将数据导入,遍历分词,并把分词结果保存在新增的特征列“split”中。
#去停用词
out = ''
for index in range(len(data)):
ct = jieba.cut(data.loc[index,'x'])
out = ''
for word in ct:
if word not in stop:
out += word
out += " "
data.loc[index,'split'] = out
#读取出数据
import pprint
text = data['split']
sentences = []
for item in text:
sentence = str(item).split(' ')
sentences.append(sentence)
将数据取出,放入series中,方便后面的word2vec模型输入
#训练
model = word2vec.Word2Vec(sentences,size = 50)
model.save('jk.model')
训练结束
def buildWordVector(imdb_w2v,text, size):
vec = np.zeros(size).reshape((1, size))
count = 0.
#print text
for word in text.split():
#print word
try:
vec += imdb_w2v[word].reshape((1, size))
count += 1.
except KeyError:
print (word)
continue
if count != 0:
vec /= count
return vec
编写文本转向量方法,将一段文字的向量用各词向量平均值来表示,为50维向量
result = buildWordVector(model, data.loc[1]['split'] , 50)
for i in range(1,len(data)):
result = np.concatenate((result, buildWordVector(model, data.loc[i]['split'] , 50)), axis = 0)
对已分词后的数据,丢入模型中进行句子的向量化操作:
#把series 转换成dataframe格式,并且将五十维的特征都赋值
vectors = pd.DataFrame(result, columns = ["x1","x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10","x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "x32", "x33", "x34", "x35", "x36", "x37", "x38" ,"x39", "x40", "x41", "x42", "x43" ,"x44", "x45", "x46" ,"x47", "x48", "x49", "x50"])
vectors
并且为了拼接在原数据中,转换为dataframe格式
#合并dataframe
data = pd.concat([data, vectors], axis = 1)
data
合并后数据:
至此,数据预处理完成
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
导入相关包
dt= data.drop(['i', 'x', 'split'],axis=1)
去掉无关字段
SSE =[]
for k in range(1,10):
model = KMeans(n_clusters=k)
model.fit(dt)
SSE.append(model.inertia_)
X = range(1,10)
plt.xlabel('k')
plt.ylabel('SSE')
plt.plot(X,SSE,'o-')
plt.show()
绘制k为1到10的肘部图,方便选取最佳k值
从图可以看出,k选3or4为最佳
#按4类分
kms=KMeans(n_clusters=4)
y4=kms.fit_predict(dt)
y4 = pd.DataFrame(y, columns = ['kind-4'])
#按3类分
kms=KMeans(n_clusters=3)
y3=kms.fit_predict(dt)
y3 = pd.DataFrame(y, columns = ['kind-3'])
分别按3、4进行训练,并分类后,拼接在原数据中
data = pd.concat([data, y3], axis = 1)
data = pd.concat([data, y4], axis = 1)
data.to_csv('data.csv', encoding="utf_8_sig")
导出,丢给其他组员以此为参考再人工分类去咯~
本文到此结束,虽然只用了knn,发现效果并不好,不过由于比赛时间原因,先将就吧,人工分完以后。日后有时间再研究怎么更好的选聚类模型,对文本进行聚类分析。
(数据涉密,不能放出,见谅)