Keras、Numpy、Pandas、Sklearn
一、背景
前面所作工作:
利用Pandas、Numpy对多数据表之间相互匹配并创建交通数据集
利用word2vec、textCNN、jieba对事故文本多分类及致因修复(三维向量)
在之前的建模中,存在一个比较大的问题就是数据量太少,真正匹配出来有对应label的文本仅仅2000来条,一部分还是需要修复的其他类,去除掉后真正用于训练的只有1300条左右。
经过TextCNN、GRU、LSTM、LSTM(改)、SVM三个模型的拟合过后效果如下:
左到右顺序分别为:拟合曲线图、混淆矩阵图、ROC曲线图
上到下顺序分别为CNN、GRU、LSTM、LSTM(改)、SVM
CNN:
GRU:
LSTM:
LSTM(改):
SVM:
可以看出,经过几十到几百次的拟合,所有模型已经几乎过拟合,这时候调各种参数的作用也不大,因此想着能不能扩大数据量,解决过拟合的问题。
因此,把眼光默默的放在了那剩下的7000多条没有label的文本上。那心情,宛如看着佳肴不能入口般难受。也罢,得想个办法吃了他们,由此想到了闻名已久的半监督学习。
所谓半监督学习,也就是指当你在拥有一个巨大的数据集时,很不巧的是真正有label的却很少,在现在的社会中这种现象也特别普遍。尽管标记员这个职业随着人工智能的普及,大幅度增多。但对于巨佬们来说,数据量还是远远不够。因此,怎么样利用少部分的label_data辅上大量的no_label_data中,这就是一个热门的问题。也因为它使用到了label_data和no_label_data,介于监督学习和无监督学习中,因此称此类训练为半监督学习。
大概普及一下知识后,咱们就进入主题,看看怎么吃掉这一堆没label的文本叭!
二、思路
先简要的把整个思路给大家说一遍。
第一步:取出之前已训练好的模型
第二步:对所有no_label_data用word2vec做向量处理,整理成同样的数据格式
第三步:循环迭代,取出no_label_datas里的一条数据,用三个模型进行预测,如果有任意俩个模型对此判别类别相同,则将此条数据标上伪标签,将其丢入训练集中,并从no_label_datas里除去。如果三个类别都不同,则将其放入no_label_datas尾部,继续预测下一条。
第四步:如此循环,直到no_label_datas里的数据全被取出,就达到了最佳状态。
所谓协同,也就是指模型之间协同训练、学习。
第五步:用最终的训练集对模型继续训练,直到拟合。
三、详细过程:
第一步:
我们将之前的模型先进行保存,.save方法直接保存模型的结构及各参数、训练好的权重。
model_cnn.save("cnn.h5")
model_lstm_1.save("lstm_1.h5")
model_lstm_1.save("lstm.h5")
并将之前训练模型划分好的训练集、测试集保存出来,用此文件读入。
X_csv_train = X_train.reshape(159720, 100)
X_csv_test = X_test.reshape(39930, 100)
X_csv_train = pd.DataFrame(X_csv_train)
X_csv_train.to_csv("X_csv_train.csv",index = False)
X_csv_test = pd.DataFrame(X_csv_test)
X_csv_test.to_csv("X_csv_test.csv", index = False)
y_csv_train = pd.DataFrame(y_train)
y_csv_train.to_csv("y_csv_train.csv", index = False)
y_csv_test = pd.DataFrame(y_test)
y_csv_test.to_csv("y_csv_test.csv", index = False)
由于pandas不能直接将三维表导出,因此先将其重构为二维表,等读入时再重构回三维。
然后另开一个jupyter文件,将模型进行导入和训练集、测试集。并用测试集记录三个模型初始评估结果。
X_train = pd.read_csv("./X_csv_train.csv")
X_train = X_train.values
X_train = X_train.reshape(1452, 110, 100)
X_test = pd.read_csv("./X_csv_test.csv")
X_test = X_test.values
X_test = X_test.reshape(363, 110, 100)
y_train = pd.read_csv("./y_csv_train.csv")
y_test = pd.read_csv("./y_csv_test.csv")
cnn:86.22%、lstm:82.92%、lstm_1:84.85%
第二步:
将no_label_datas进行预处理,转为向量形式
#导包
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
from sklearn.preprocessing import label_binarize
data = pd.read_excel("./wenben.xls")
data
#判断字符串是不是中文
def check_contain_chinese(check_str):
for ch in check_str:
if u'\u4e00' <= ch <= u'\u9fff':
return True
else:
return False
#分词
import jieba
jieba.load_userdict('../dic.txt')
stop = [line.strip() for line in open('../stopwords.txt').readlines()]
#去停用词
out = ''
for index in range(len(data)):
ct = jieba.cut(data.loc[index,'x'])
out = ''
for word in ct:
if word not in stop:
if check_contain_chinese(word) == True:
if (word.endswith("省") == False):
if (word.endswith("市") == False):
if (word.endswith("县") == False):
if (word.endswith("镇") == False):
if (word.endswith("村") == False):
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)
#训练
model = word2vec.Word2Vec(sentences,size = 100)
model.save('jk.model')
用所有文本重新训练word2vec模型。
def buildWordVector(imdb_w2v,text, size):
vec = np.zeros(size).reshape((1, size))
pad = np.zeros(size).reshape((1, size))
count = 0
for word in text.split():
try:
vec = np.vstack((vec, imdb_w2v[word].reshape((1, size))))
count += 1
except KeyError:
print (word)
vec = np.delete(vec, 0, 0)
if len(vec) < 110:
for i in range(110 - len(vec)):
vec = np.vstack((vec, pad))
else:
vec = vec[0:110]
return vec
model_word = word2vec.Word2Vec.load('./jk.model')
result = buildWordVector(model_word, data.loc[1]['split'] , 100)
for i in range(1,len(data)):
result = np.concatenate((result, buildWordVector(model_word, data.loc[i]['split'] , 100)), axis = 0)
result.shape
x_all = result.reshape(len(data),110,100)
至上,转换完毕。
第三步:
循环迭代,妈的,这一段折腾死人,python里的list、array、DataFrame这几种类型增删改查的方法都不一样,对array的操作,百度排第一的就是个智障,居然还有这么多的阅览数,完全把list当array弄,坑死人。
直接贴代码吧就
while len(x_all) != 0:
#取出no_label_datas的第一条数据
x_pre = x_all[0]
#因为后面拼接需要,这里特别蛋疼的需要把(110,100)重构为(1,11,100)
x_pre.resize(1,110,100)
#用模型进行类别预测后,便于后面拼接,重新二值化
type_cnn = label_binarize(cnn.predict_classes(x_pre), classes = [0,1,2])
type_lstm = label_binarize(lstm.predict_classes(x_pre), classes = [0,1,2])
type_lstm_1 = label_binarize(lstm_1.predict_classes(x_pre), classes = [0,1,2])
#flag变量
right = False
if np.argmax(type_cnn) == np.argmax(type_lstm):
y_pre = type_cnn
right = True
elif np.argmax(type_cnn) == np.argmax(type_lstm_1):
y_pre = type_cnn
right = True
elif np.argmax(type_lstm) == np.argmax(type_lstm_1):
y_pre = type_lstm
right = True
if(right):
#如果符合条件,蛋疼的拼接到训练集中
X_train = np.vstack([X_train, x_pre])
#伪标签也时,拼拼拼
y_train = pd.concat([y_train, pd.DataFrame(y_pre, columns = ['0','1','2'])],ignore_index = True)
print("cnn: ")
#意思意思,三个模型都迭代一次
cnn.fit(X_train, y_train.values,epochs=1,batch_size = 128, validation_data=(X_test, y_test.values))
print("lstm: ")
lstm.fit(X_train, y_train.values,epochs=1,batch_size = 128, validation_data=(X_test, y_test.values))
print("lstm_1: ")
lstm_1.fit(X_train, y_train.values,epochs=1,batch_size = 128, validation_data=(X_test, y_test.values))
#删除掉这一条
x_all = x_all[1:]
right = False
else:
#把这条放在最后
x_all = x_all[1:]
x_all[len(x_all)] = x_pre
print("The length of the no_label_data is : " + str(len(x_all)))
然后就关屏幕,等它跑吧,算了一下大概需要的时间,cnn1秒,lstm2秒,lstm(改)2秒,就算每一条都符合,也要9000*(1+2+2) / 60 / 60 = 12.5小时....睡一觉,明早应该就好了吧可能。
这样就到了最后一步:
代码就不贴了,就是正常的fit(xxxxxx),然后就调库画个过程图看看这次半监督的效果叭,暂时开来,效果挺好的,还没有经过进一步的训练就已经有所提升。
至上,这次比赛我所需要做的大概也就差不多完了。
整个持续了3-6月,从校初赛、校决赛、国初赛,到本月25号即将到来的国决赛,收获满满,也希望这次能有个完美的结局,也算对得起这段时间想破头皮的写代码了。
最后,惯例还是:
(数据涉密,不能外放,见谅!)