目前已经广泛使用深度学习的方法进行数据分类,包括文本、图像、声音和视频等。其中Tensorflow是使用最为广泛的一种深度学习框架,其能够实现分布式以及使用GPU提高建立模型的速度,但其缺点是代码流程较为复杂,而Keras实现了对Tensorflow API的高层封装,使其相比Tensorflow更加简单易用,目前Keras已经作为Tensorflow中的一部分与Tensorflow一起发布。
为了进行模型的训练过程,需要获得一定量的样本数据,本文中的样本数据来自某问答平台,将用户提问标题的文本和问题所属的分类作为学习的训练集。目前Python的pyquery库很好的模拟了jQuery的风格,相比使用正则表达式,其能够更便利的从html文本中提取到所需位置的数据,获取数据的脚本如下
k = 0; #对于每个分类的地址将获取到的问题写入一个文件 for url in urls: k += 1; fp = open(str(k) + '.txt', 'w'); #获取该分类下的问题总数 request = urllib.request.Request(url = url, headers = headers); try: res = urllib.request.urlopen(request, timeout=3); html = res.read().decode('gbk'); except Exception as e: print(e); continue; #计算页数 num = int(jQuery(html).find('span.count-num')[0].text); page = num // size; #按页进行数据获取 for i in range(0, page): request = urllib.request.Request(url = url + '&rn=' + str(size) + '&pn=' + str(i * size), headers = headers); #如果出现错误就放弃当页的数据,防止整个程序退出 try: res = urllib.request.urlopen(request, timeout=3); html = res.read().decode('gbk'); except Exception as e: print(e); continue; #通过pyquery获取保存问题标题的a标签 for x in jQuery(html).find('a.title-link'): fp.write(x.text.strip() + "\n"); time.sleep(2); fp.close();
生成训练模型的思路为使用经典和简单的TF-IDF方法(这里没有使用目前更加流行的word2vec基于卷积神经网络的方法),对每个问题的标题进行分词,并计算出主要词汇的TF-IDF值生成对应每个问题的向量,问题所属的分类则作为该问题的分类值。
首先需要根据全部的问题生成总的词汇表——每个词汇对应一个唯一的索引值(0~词汇表长度-1),同时也决定了对于每一个问题对应的向量的长度,即train_x数组的维度为“问题个数x词汇表的长度”,train_y数组的维度则为“问题个数x1”,其每一个元素为该个问题的分类索引值。训练过程的代码如下
import jieba.analyse; import numpy; import pickle; import tensorflow.keras; import scipy.sparse; from tensorflow.keras.layers import Dense, Activation; if __name__ == '__main__': category = 20; k = 0; j = 0; #生成词汇表 words = dict(); for i in range(0, category): #对于每个文件(该分类)中的问题 fp = open(str(i + 1) + '.txt'); for x in fp: j += 1; #进行TF-IDF分词,IDF使用分词引擎中的数据 for y in jieba.analyse.extract_tags(x, withWeight=True): #如果是新词,则更新词汇表 if y[0] not in words: words[y[0]] = k; k += 1; fp.close(); #将词汇表保存,以便于预测时使用 pickle.dump(words, open('words.bin', 'wb')); #确定了两个数组的维度,这里使用稀疏矩阵可以节约一定的内存空间 train_x = scipy.sparse.lil_matrix((j, len(words))); train_y = numpy.zeros((j, 1)); j = 0; #将分类索引值和TF-IDF值填入对应的数组中 for i in range(0, category): fp = open(str(i + 1) + '.txt'); for x in fp: #填入分类索引值 train_y[j] = i; for y in jieba.analyse.extract_tags(x, withWeight=True): #填入TF-IDF train_x[j, words[y[0]]] = y[1]; j = j + 1; #初始化模型 model = tensorflow.keras.Sequential(); #设置网络 model.add(Dense(32, activation='relu', input_dim=len(words))); model.add(Dense(category, activation='sigmoid')); #设置分类的优化器 model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy']); #处理分类索引值数组 train_y = tensorflow.keras.utils.to_categorical(train_y, num_classes=category); #开始训练 model.fit(train_x, train_y, epochs=10, batch_size=32); #保存训练得到的模型 model.save('1.h5');
运行训练过程的输出结果如下,可见10轮训练后的准确率就比较高了
Epoch 1/10 14921/14921 [==============================] - 4s 289us/sample - loss: 2.9050 - accuracy: 0.2525 Epoch 2/10 14921/14921 [==============================] - 4s 269us/sample - loss: 2.3243 - accuracy: 0.6018 Epoch 3/10 14921/14921 [==============================] - 4s 273us/sample - loss: 1.5976 - accuracy: 0.7354 Epoch 4/10 14921/14921 [==============================] - 4s 275us/sample - loss: 1.1616 - accuracy: 0.7962 Epoch 5/10 14921/14921 [==============================] - 4s 274us/sample - loss: 0.8692 - accuracy: 0.8380 Epoch 6/10 14921/14921 [==============================] - 4s 271us/sample - loss: 0.6639 - accuracy: 0.8674 Epoch 7/10 14921/14921 [==============================] - 4s 272us/sample - loss: 0.5143 - accuracy: 0.8899 Epoch 8/10 14921/14921 [==============================] - 4s 267us/sample - loss: 0.4068 - accuracy: 0.9089 Epoch 9/10 14921/14921 [==============================] - 4s 267us/sample - loss: 0.3283 - accuracy: 0.9220 Epoch 10/10 14921/14921 [==============================] - 4s 267us/sample - loss: 0.2700 - accuracy: 0.9335
训练得到模型后就可以使用模型对新的问题进行分类预测了,预测的脚本如下
#读取词库 words = pickle.load(open('words.bin', 'rb')); #加载模型 model = load_model('1.h5'); #需要预测的文本信息,由命令行参数给出 text = sys.argv[1]; #输入向量的维度为1x词汇表长度 data = numpy.zeros((1, len(words))); #分词并计算TF-IDF for x in jieba.analyse.extract_tags(text, withWeight=True): #如果词汇表中有此词汇则将TF-IDF填入输入向量中 if x[0] in words: data[0, words[x[0]]] = x[1]; #预测,并取出可能性最高的一个分类的索引值 i = numpy.argmax(model.predict(data)); print(labels[i]);
尝试了几个问题,得到的分类结果如下,总体感觉似乎还可以。
健康猫爪子会不会沾染狂犬病毒,请看下面的补充评论谢谢。 -> 生活 平板网络很好,为什么有的软件显示网络不可用?早上还可以! -> 家电数码 普耐尔250二氧化碳焊机手工焊起弧电流什么凋? -> 生产制造 中国的地标城市是在那个地方? -> 旅游