之前也提过,pytorch之于tensorflow,优雅不是一星半点。
pytorch学习是轻松愉快了。本期文章写pytorch在nlp上的应用。(paul allen的allennlp也由tensorflow迁移到了pytorch平台,可见一斑)
导入模块:
import torch
import torch.autograd as autograd
import torch.nn as nn
import torch.optim as optim
torch.manual_seed(1)
一、线性模块,这里需要注意是y = xw+b。不是我们数学上习惯的y=wx+b(矩阵是不满足乘法交换率的)
定义一个全连接层,也就是线性仿射变换。W=(5,3),b=(3)
lin = nn.Linear(5,3)
这里需要特别注意,输入变量是n*5,输出为n*3
data = autograd.Variable(torch.randn(2,5))
lin(data)
运算得到结果如下:
Variable containing:-0.2889 0.3574 0.6554-0.9682 0.0289 0.4426[torch.FloatTensor of size 2x3]
二、非线性激活函数
常用的Relu和tanh。sigmoid因为梯度消失的问题,反而不常用。从理论上讲,非线性的激活函数可以无穷多,为什么选这两个?这是工程上的问题,因为它们的导数形式便于计算。
我们用随机正态分布,初始化2*2的矩阵。
data = autograd.Variable(torch.randn(2,2))
Variable containing:-0.6331 0.8795-0.6842 0.4533[torch.FloatTensor of size 2x2]
把变量经由relu计算(Rectified Linear Unit,修正线性单元),relu的公式很简单=max(0,x),就是如果x>0,就取x,否则取0,就是把负的信号给屏蔽了。
F.relu(data)
Variable containing: 0.0000 0.8795 0.0000 0.4533[torch.FloatTensor of size 2x2]
下图的蓝线是relu
对比sigmoid类函数主要变化是:
1)单侧抑制
2)相对宽阔的兴奋边界
3)稀疏激活性。
这与人的神经皮层的工作原理接近。
另外一个很重要的激活函数就是softmax。
data = autograd.Variable(torch.randn(2,2))
Variable containing:-0.3968 -0.6571-1.6428 0.9803[torch.FloatTensor of size 2x2]
F.softmax(data,dim=0),dim=0,按第0维softmax,就是概率化。这个函数在输出层很有用,比如mnist要分成10类,那softmax就是这10类对应的概率。概率最大者就是对应的分类。(概率思维也在这里体现了)
Variable containing: 0.7766 0.1628 0.2234 0.8372[torch.FloatTensor of size 2x2]
还有一个类似的函数F.log_softmax()也是工程上的考虑,因为最小二乘损失函数求导要会带来计算量的问题,所以考虑交叉熵损失。与之配合的就是这个对数softmax。
三、目标函数。也就是反身传播要优化的目标。也称为成本函数或损失函数。
四,词袋模型(Bag-Of-Words=BOW),应该算最简单的语言模型,不考虑词的位置,就是数数。比如词汇表如果就两个词,hello,world。那么"hello hello hello hello" = [4,0],'hello world hello world'=[2,2],即所谓语言模型,就是对一个字符串,或文章内容,如何进行编码,转为计算机和pytorch可以处理的向量或矩阵类型。这里的数字就是表达词的出现次数。
用这个最简单的BOW,来对西班牙语和英语做分类。
先定义最简单的训练语料和测试语料。
data=[("me gusta comer en la cafeteria".split(),"SPANISH"),("Give it to me".split(),"ENGLISH"),("No creo que sea una buena idea".split(),"SPANISH"),("No it is not a good idea to get lost at sea".split(),"ENGLISH")]
test_data=[("Yo creo que si".split(),"SPANISH"),("it is lost on me".split(),"ENGLISH")]
把词下标化,计算机其实不会真的去管这个词是什么东西,有什么语义。现代统计语言模型或深度学习,确实没有管单词本身具有什么意思,而是“统计”它们出现的位置,频率,前后关系等。就已经解决了很多问题。(语义的环境,应该得交由知识图谱)
首选把词汇表单词下标算出来。
word_2_idx = {}
for sent,_ in data + test_data:
print(sent)
for word in sent:
if word not in word_2_idx:
#这里用的是这个word首次出现的位置,比如me就是第1个出现的,下标为0
word_2_idx[word] = len(word_2_idx)
有了词汇表,就可以算词汇表的长度,以及我们需要把句子分成两类。
VOCAB_SIZE = len(word_2_idx)
NUM_LABELS = 2
神经网络模型:
class BoWClassifier(nn.Module): # 从nn.Module继承bow分类器
def __init__(self, num_labels, vocab_size):
#父类初始化
super(BoWClassifier, self).__init__()
self.linear = nn.Linear(vocab_size, num_labels)
def forward(self, bow_vec):
#bow_vec是词的bow向量
return F.log_softmax(self.linear(bow_vec))
把句子变成向量:
就是用句子里的词,去词汇表里查下标,在向量对应的位置加1。
def make_bow_vector(sentence, word_to_ix):
vec = torch.zeros(len(word_to_ix))
for word in sentence:
vec[word_to_ix[word]] += 1
#vec本来是个向量,变形成1*N的矩阵
return vec.view(1, -1)
make_bow_vector("Give it to me".split(),word_2_idx)
在用户手动定义Variable时,参数requires_grad默认值是False。而在Module中的层在定义时,相关Variable的requires_grad参数默认是True。
在计算图中,如果有一个输入的requires_grad是True,那么输出的requires_grad也是True。只有在所有输入的requires_grad都为False时,输出的requires_grad才为False。
关于作者:魏佳斌,互联网产品/技术总监,北京大学光华管理学院(MBA),特许金融分析师(CFA),资深产品经理/码农。偏爱python,深度关注互联网趋势,人工智能,AI金融量化。致力于使用最前沿的认知技术去理解这个复杂的世界。
领取专属 10元无门槛券
私享最新 技术干货