【实战】使用神经网络和TensorFlow来对文本分类

1.机器学习算法 + 数据 = 预测模型

由于我们定义的模型是用来对文本进行分类,所以定义如下:
输入: 文本,
输出: 分类

接着,我们需要一个做了标记的文本训练集(每个文本对应一个分类标志)。在机器学习里,这种情况叫做监督学习(Supervised learning)。

由于是把数据分类,所以这个又是分类任务。为了创建这个模型,我们将使用神经网络。

2.神经网络

神经网络的灵感来自我们的中枢神经系统,相互连接的节点就像我们的神经单元一样:

image

2.1神经网络架构

本神经网络由两个隐藏层组成(至于选择多少层隐藏层,这是属于神经网络的架构设计)。每个隐藏层的工作是将输入转换成输出层可以使用的东西。

  • 第一层隐藏层
    你需要定义第一层隐藏层有多少个节点,这些节点也叫做特征值或者神经元,在上图里表示为圆圈。

每个节点(神经元)都乘以一个权重值。每个节点有一个权重值,在训练期间,神经网络通过调整这些权重值,以便可以生产一个正确的输出。
除了每个输入节点乘以权重值之外,神经网络还需要加上一个偏差值

在我们的构架里输入的值乘以权重值,加上偏差值,然后通过激活函数。激活函数是定义在每个输出节点的后面,可以这样来理解:假设每个节点都是灯,激活函数告诉灯是否亮。

激活函数有很多种类型,最常使用的非线性激活函数(ReLu),它定义如下:
图片:
https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1516426898949&di=67340e2c677b2c6a855891f47907987c&imgtype=0&src=http%3A%2F%2Fimg.mp.itc.cn%2Fupload%2F20161013%2F417f598b58914ed6a46a714c05b79f6f.png

  • 第二层隐藏层

其实第二层隐藏层的工作方式与第一层是一样的,只不过,第二层是从第一层进行输入的:


image
  • 输出层
    需要使用one-hot-encoding(独热编码
    )来表示输出结果,仅有一个元素为1,其它元素为0.假如,我们使用三个分类(体育、太空和计算机图形学):

由上可知,输出节点的数目就是数据集分类的数目。

+-------------------+-----------+
| category | value |
+-------------------|-----------+
| sports | 001 |
| space | 010 |
| computer graphics | 100 |

输出层的数值也是乘以权重值,并加上偏差值,但最后的激活函数是不一样的。

您希望将每个文本标记为一个类别,这些类别是相互排斥的(意味着每个文本不可能同时属于两种类别)。考虑到这些,显然使用非线性激活函数ReLu就不行了,因而采用Softmax函数,这个函数转换输出结果为0和1之间,且所有元素相加起来等于1,通过这样的方式告诉我们每个分类的文本的概率:

Softmax函数

| 1.2 0.46|
| 0.9 -> [softmax] -> 0.34|
| 0.4 0.20|

到这里已经把全部神经网络的数据流图说完了,可根据整个过程转换为代码,如下:

# -*- coding: utf-8 -*-
"""
Created on Sat Jan 20 11:03:16 2018

@author: JayMo
"""

# Network Parameters
n_hidden_1 = 10        # 1st layer number of features
n_hidden_2 = 5         # 2nd layer number of features
n_input = total_words  # Words in vocab
n_classes = 3          # Categories: graphics, space and baseball
def multilayer_perceptron(input_tensor, weights, biases):
    layer_1_multiplication = tf.matmul(input_tensor, weights['h1'])
    layer_1_addition = tf.add(layer_1_multiplication, biases['b1'])
    layer_1_activation = tf.nn.relu(layer_1_addition)
# Hidden layer with RELU activation
    layer_2_multiplication = tf.matmul(layer_1_activation, weights['h2'])
    layer_2_addition = tf.add(layer_2_multiplication, biases['b2'])
    layer_2_activation = tf.nn.relu(layer_2_addition)
# Output layer with linear activation
    out_layer_multiplication = tf.matmul(layer_2_activation, weights['out'])
    out_layer_addition = out_layer_multiplication + biases['out']
return out_layer_addition

2.2神经网络怎么样学习

从上面我们看到,权重值是在神经网络训练的过程中更新的,下面通过TensorFlow的环境里来查看神经网络是怎么样学习的。

tf.Variable
权重值和偏差值都是保存在变量(tf.Variable)里,通过调用函数run()来维护和更新这些变量的状态。在机器学习的初始阶段,我们常常把这些权重值和偏差值初始化为正态分布的值

weights = {

    'hidden1': tf.Variable(tf.random_normal([n_input, n_hidden_1])),

    'h2': tf.Variable(tf.random_normal([n_hidden_1, n_hidden_2])),

    'out': tf.Variable(tf.random_normal([n_hidden_2, n_classes]))

}

biases = {

    'biases1': tf.Variable(tf.random_normal([n_hidden_1])),

    'b2': tf.Variable(tf.random_normal([n_hidden_2])),

    'out': tf.Variable(tf.random_normal([n_classes]))

}

当我们第一次运行神经网络时,这些权重值和偏差值都采用正态分布的值来初始化:

  • input values: x
  • weights: w
  • bias: b
  • output values: z
  • expected values: expected

神经网络为了知道怎么样学习,需要比较输出值(z)和期望值(expected)之间的差异,然后通过计算它们之间的差(损失)?计算这种之间的差别有很多方式,但我们这里的任务是分类任务,因此最好的损失函数是采用交叉熵的方式

在TensorFlow里用来计算交叉熵的函数是tf.nn.softmax_cross_entroy_with_logits(),接着使用平均误差tf.reduced_mean()来计算:

# Construct model
prediction = multilayer_perceptron(input_tensor, weights, biases)

# Define loss
entropy_loss = tf.nn.softmax_cross_entropy_with_logits(logits=prediction, labels=output_tensor)
loss = tf.reduce_mean(entropy_loss)

为了让输出的误差最小化(输出值与期望值之间的差最小),就需要找到合适的权重值和偏差值。要完成这个任务,因而就引入了梯度下降的算法,更加直接一点就是采用随机梯度下降算法


image

同样也有很多算法来计算梯度下降的,在这里采用Adaptive Moment Estimation (Adam,自适应矩估计)算法计算。在TensorFlow里使用这个算法时,需要输入一个学习速率的参数,这个参数决定了每一次找到最好的权重值的步伐。

方法tf.train.AdamOptimizer(learning_rate).minimize(loss)是分成两步计算的,如下:

  1. 计算梯度(损失值, <变量列表>)
  2. 更新梯度(<变量列表>)

这个方法更新了所有变量tf.Variables为新值,所以不需传送变量列表。可以把训练的代码像下面这样编写:

learning_rate = 0.001  
# Construct model  
prediction = multilayer_perceptron(input_tensor, weights, biases)  
# Define loss  
entropy_loss = tf.nn.softmax_cross_entropy_with_logits(logits=prediction, labels=output_tensor)  
loss = tf.reduce_mean(entropy_loss)  
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(loss) 

2.3数据操作

在这个数据集里使用英语文本来表示,需要把这些数据通过神经网络,下面需要做两件事情:

  1. 每个单词创建一个索引
  2. 每句话创建一个矩阵,如果单词出现标记为1,否则标记为0.
import numpy as np    #numpy is a package for scientific computing  
from collections import Counter  
vocab = Counter()  
text = "Hi from Brazil"  
#Get all words  
for word in text.split(' '):  
    vocab[word]+=1  
          
#Convert words to indexes  
def get_word_2_index(vocab):  
    word2index = {}  
    for i,word in enumerate(vocab):  
        word2index[word] = i  
          
    return word2index  
#Now we have an index  
word2index = get_word_2_index(vocab)  
total_words = len(vocab)  
#This is how we create a numpy array (our matrix)  
matrix = np.zeros((total_words),dtype=float)  
#Now we fill the values  
for word in text.split():  
    matrix[word2index[word]] += 1  
print(matrix)  
>>> [ 1.  1.  1.]  
  
在这个例子里,句子“Hi from Brazil”变成矩阵[1. 1. 1.]表示,如果句子只有单词“Hi”时怎么样表示?  
matrix = np.zeros((total_words),dtype=float)  
text = "Hi"  
for word in text.split():  
    matrix[word2index[word.lower()]] += 1  
print(matrix)  
>>> [ 1.  0.  0.] 

对于标签也可采用同样方法,不过是采用one-hot向量的方式:

y = np.zeros((3),dtype=float)
if category == 0:
    y[0] = 1.        # [ 1.  0.  0.]
elif category == 1:
    y[1] = 1.        # [ 0.  1.  0.]
else:
     y[2] = 1.       # [ 0.  0.  1.]

2.4运行数据流图和获取计算结果

  • 数据集
    可以使用20个新闻组(http://qwone.com/~jason/20Newsgroups/)的数据,20个主题组成,大概有18000个篇文章。要加载这些数据需要使用scikit-learn库,在这里仅使用三个分类:comp.graphics, sci.space 和rec.sport.baseball。Scikit-learn有两个集合组成:一个训练集和测试集。建议你不要看测试数据,因为在创建模型时会干扰你的选择。你不想创建一个模型来预测这个特定的测试数据,你想创建一个模型,具有良好的泛化。

下面就是加载数据的代码:

from sklearn.datasets import fetch_20newsgroups
categories = ["comp.graphics","sci.space","rec.sport.baseball"]
newsgroups_train = fetch_20newsgroups(subset='train', categories=categories)
newsgroups_test = fetch_20newsgroups(subset='test', categories=categories)
  • 训练模型

在神经网络的术语里,一个周期等于一遍数据通过(获取输出值)和一遍反馈(更新权重值)。

还记得方法tf.Session.run()吗?让我们再来仔细看一下:
tf.Session.run(fetches, feed_dict=None, options=None, run_metadata=None)

在刚开始的神经网络的数据流图,我们使用相加的操作,但现在我们能传送一系列的操作了。在神经网络里,你主要做两件事情:损失值计算和每一步优化。

参数feed_dict是每一步运行时的传送数据的参数,为了传送数据,需要使用类(tf.placeholders)定义变量。

n_input = total_words # Words in vocab

n_classes = 3         # Categories: graphics, sci.space and baseball

input_tensor = tf.placeholder(tf.float32,[None, n_input],name="input")

output_tensor = tf.placeholder(tf.float32,[None, n_classes],name="output")

训练时可以使用分批进行:

我们将字典具有较大的批量测试时的模型,这就是为什么你需要定义一个变量的批处理尺寸。

因此定义函数get_batches()来获取批量:

training_epochs = 10
# Launch the graph
with tf.Session() as sess:
    sess.run(init) #inits the variables (normal distribution, remember?)
    # Training cycle
    for epoch in range(training_epochs):
        avg_cost = 0.
        total_batch = int(len(newsgroups_train.data)/batch_size)
        # Loop over all batches
        for i in range(total_batch):
            batch_x,batch_y = get_batch(newsgroups_train,i,batch_size)
            # Run optimization op (backprop) and cost op (to get loss value)
            c,_ = sess.run([loss,optimizer], feed_dict={input_tensor: batch_x, output_tensor:batch_y})

到这里已经有一个被训练的模型了,为了测试它,必须创建一个测试运行图。我们将测量模型的准确性,所以你需要得到预测值的指数和正确值的索引(因为我们使用的是一个热编码),检查它们是否相等,并计算所有测试数据集的平均值:

# Test model

    index_prediction = tf.argmax(prediction, 1)

    index_correct = tf.argmax(output_tensor, 1)

    correct_prediction = tf.equal(index_prediction, index_correct)

    # Calculate accuracy

    accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

    total_test_data = len(newsgroups_test.target)

    batch_x_test,batch_y_test = get_batch(newsgroups_test,0,total_test_data)

    print("Accuracy:", accuracy.eval({input_tensor: batch_x_test, output_tensor: batch_y_test}))

到这里,我们已经使用神经网络来创建分类的任务。值得庆贺一下!

可以在这里看到完整的代码(可以修改我们定义的值来查看怎么样影响训练时间和模型的精度):https://github.com/dmesquita/understanding_tensorflow_nn

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 原文链接:https://yq.aliyun.com/articles/178374 0. 简介 在过去,我写的主...
    dopami阅读 5,635评论 1 3
  • 我有很多群:“大学群、中学群、小学群、工友群、朋友群、生意群、创业群、运动群、唱歌群……” 然而,我最热衷的还是...
    阿1尚卫国阅读 892评论 3 3
  • 忽然觉得自己可以重回校园时光,认真专注的做一件事情,每天的生活极简到就剩下那么一件事,只要想去做,就可以做到。那是...
    每天励志的懒猫阅读 210评论 0 0
  • 你这样 我也挺尴尬的 我不想撩你了 只想直接问你 喜欢男生还是喜欢女生
    翮笙阅读 138评论 0 0