pytorch进行深度学习(二)
1、深度学习构建基块
深度学习包括以巧妙的方式将线性与非线性组成。非线性的引入允许强大的模型。在本节中,我们将使用这些核心组件,组成目标函数,并了解如何训练模型。深度学习构建基块:仿射图,非线性和目标
1.1、仿射线性函数
仿射图是深度学习的核心动力之一,仿射图是一种功能 f(x) :
对于矩阵 A和向量 x,b。这里要学习的参数是A和 b。经常,b被称为偏差项。
PyTorch和大多数其他深度学习框架的功能与传统线性代数略有不同。它映射输入的行而不是列。也就是,输出的第i行对应输入A的第行,加上偏差项,看下面的例子。
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
torch.manual_seed(1)
'''实例:声明一个20维到10维的映射函数'''
lin = nn.Linear(10, 2) # 数学函数为:`y = xA^T + b`, parameters A, b;nn.Linear(in_features:int, out_features:int, bias:bool=True) -> None
input = torch.randn(20, 10)
output = lin(input)
print(output.size()) #A为10*2的映射矩阵;输出torch.Size([20, 2])
print(output)
1.2、非线性函数
首先,请注意以下事实,这将解释为什么我们首先需要非线性。假设我们有两个仿射图 f(x)=Ax+b和 g(x)=Cx+d。什么是 f(g(x))?
AC是一个矩阵, Ad+b是向量,因此我们看到组成仿射图会为您提供仿射图。
从中可以看出,如果您希望神经网络是仿射成分的长链,那么仅制作一个仿射图就不会为模型增加新的功能。
如果我们在仿射层之间引入非线性,则不再是这种情况,我们可以构建功能更强大的模型。
有一些核心非线性。 tanh(x),σ(x),ReLU(x)是最常见的。您可能想知道:“为什么要使用这些功能?我可以想到很多其他非线性因素。” 这是因为它们具有易于计算的梯度,并且计算梯度对于学习至关重要。例如
快速说明:尽管您可能在AI入门课程中学习了一些神经网络,但其中 σ(x)是默认的非线性,通常人们在实践中会回避它。这是因为随着参数的绝对值增加,梯度会很快消失。小梯度意味着很难学习。大多数人默认使用tanh或ReLU。
'''
在pytorch中大部分非线性函数在torch.nn.functional中,非线性函数没有映射参数;在训练过程中无需更新权重。
'''
data = torch.randn(2, 2)
print(data) #输出tensor([[-0.4519, -0.1661],[-1.5228, 0.3817]])
print(F.relu(data)) #输出tensor([[0.0000, 0.0000],[0.0000, 0.3817]])
1.3、Softmax和概率
功能 Softmax(x)也是非线性的,但是它的特殊之处在于它通常是网络中的最后一个操作。这是因为它接受实数向量并返回概率分布。其定义如下。让 xx是实数的向量(正数,负数,等等,没有约束)。然后是 Softmax(x)Softmax(x) 是
应该清楚的是,输出是概率分布:每个元素都是非负的,所有分量的总和为1。
您也可以将其视为仅对输入应用按元素取幂的运算符,以使所有内容均为非负值,然后除以归一化常数。
# Softmax也在函数torch.nn.functional
data = torch.randn(5)
print(data)
print(F.softmax(data, dim=0))
print(F.softmax(data, dim=0).sum()) # Sums to 1 because it is a distribution!
print(F.log_softmax(data, dim=0)) # theres also log_softmax
1.4、目标函数
目标函数是训练网络以使其最小化的函数(在这种情况下,通常称为损失函数 或成本函数)。首先,选择一个训练实例,通过您的神经网络运行它,然后计算输出损失。然后,通过采用损失函数的导数来更新模型的参数。凭直觉,如果您的模型对答案完全有信心,而答案是错误的,那么您的损失将会很高。如果它对答案非常有信心,并且答案正确,那么损失将很小。
最小化训练示例中的损失函数的想法是,您的网络有望很好地推广,并且在开发集,测试集或生产环境中的未见示例中损失很小。损失函数的一个示例是负对数似然损失,它是多类分类的一个非常常见的目标。对于有监督的多类分类,这意味着训练网络以最小化正确输出的负对数概率(或等效地,最大化正确输出的对数概率)。
2、优化与训练
那么我们可以为实例计算损失函数吗?我们该怎么办?我们之前已经看到过Tensors知道如何相对于用于计算梯度的事物来计算梯度。由于我们的损失是张量,因此我们可以针对用于计算它的所有参数计算梯度!然后,我们可以执行标准梯度更新。让θ 作为我们的参数, L(θ)损失函数,以及 η积极的学习率。然后:
尝试进行更多的操作而不仅仅是这种原始梯度更新的方法,还有大量的算法和研究。许多人尝试根据训练时的变化来改变学习率。除非您真的很感兴趣,否则您不必担心这些算法在做什么。Torch在torch.optim包中提供了许多功能,并且它们都是完全透明的。使用最简单的梯度更新与更复杂的算法相同。尝试使用不同的更新算法和不同的更新算法参数(例如不同的初始学习率)对于优化网络性能非常重要。通常,仅用Adam或RMSProp之类的优化器替换SGD即可显着提高性能。
3、在PyTorch中创建网络组件
在继续关注NLP之前,让我们做一个带注释的示例,该示例仅使用仿射图和非线性关系在PyTorch中构建网络。我们还将看到如何使用PyTorch内置的负对数似然来计算损失函数,以及如何通过反向传播更新参数。
所有网络组件都应继承自nn.Module并重写forward()方法。从nn.Module继承为您的组件提供功能。例如,它可以跟踪可训练的参数,可以使用.to(device)
方法在CPU和GPU之间切换,其中设备可以是CPU设备torch.device("cpu")
或CUDA设备torch.device("cuda:0")
。
3.1、逻辑回归词袋分类器
我们的模型将映射稀疏的BoW表示形式以记录标签上的概率。我们为词汇中的每个单词分配一个索引。例如,假设我们的整个词汇是两个单词“ hello”和“ world”,分别具有索引0和1。句子“ hello hello hello hello”的BoW向量是[4,0]对于“ hello world world hello”,它是[2,2]等,通常是
a)词向量表示
[Count(hello),Count(world)]将此BOW向量表示为 x。
b) 逻辑回归表示
输出为:logSoftmax(Ax+b)
也就是说,我们通过仿射映射传递输入,然后记录softmax。
c)定义目标函数
传递实例以获取对数概率,计算损失函数,计算损失函数的梯度,然后使用梯度步长更新参数。损耗功能由nn包中的Torch提供。nn.NLLLoss()是我们想要的负对数似然损失。它还在torch.optim中定义了优化功能。在这里,我们将仅使用SGD。
请注意,NLLLoss的输入是对数概率的向量和目标标签。它不会为我们计算对数概率。这就是为什么我们网络的最后一层是log softmax。损失函数nn.CrossEntropyLoss()与NLLLoss()相同,不同之处在于它为您执行了log softmax。
'''
bow构建词向量;简单的逻辑分类
训练数据:4条训练数据,两个标签SPANISH,ENGLISH
测试数据:2条测试数据,两个标签
'''
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")]
'''
构建词和id的映射关系
'''
word_to_ix = {}
for sent, _ in data + test_data:
for word in sent:
if word not in word_to_ix:
word_to_ix[word] = len(word_to_ix)
print("词典word_to_ix:",word_to_ix)
VOCAB_SIZE = len(word_to_ix)
NUM_LABELS = 2
'''
定义一个BowClassifier分类器nn.Module
'''
class BowClassifier(nn.Module):
def __init__(self,num_labels,vocab_size):
'''
调用nn.Module的初始化函数
'''
super(BowClassifier,self).__init__()
self.linear=nn.Linear(vocab_size,num_labels) #定义线性分类器
def forward(self,bow_vec):
'''
重写前向传播函数
bow_vec:词向量
'''
return F.softmax(self.linear(bow_vec),dim=1)
'''
构造bow词典
'''
def make_bow_vec(sen,word2id):
vec=torch.zeros(len(word2id))
for word in sen:
vec[word2id[word]]+=1
return vec.view(1,-1)
def make_target(label, label_to_ix):
print(torch.LongTensor([label_to_ix[label]]))
return torch.LongTensor([label_to_ix[label]])
model=BowClassifier(NUM_LABELS,VOCAB_SIZE)
'''
遍历参数
'''
def print_param_steps(model,step):
for param in model.parameters():
print("step:{},param:{} ".format(step,param))
'''
获取forward返回的结果softmax预测概率
'''
with torch.no_grad():
sample=data[0] #msg,label
vec=make_bow_vec(sample[0],word_to_ix)
print("vec size:",vec.size())
prob=model(vec)
print("prob size:",prob)
label_to_ix = {"SPANISH": 0, "ENGLISH": 1}
#定义损失函数公式
loss_function=nn.NLLLoss()
opt=optim.SGD(model.parameters(),lr=0.1)
#训练
i=0
for epoch in range(5):
for inst,label in data:
#1、PyTorch累积梯度,初始时需将梯度清零
print_param_steps(model,i) #每更新一个示例,参数更新一次
model.zero_grad()
#2、构建bow向量,获取标签数字
vec=make_bow_vec(inst,word_to_ix)
target=make_target(label,label_to_ix)
#3、执行并前向传播
probs=model(vec)
#4、计算损失函数,梯度,调用optimizer.step()更新参数
loss=loss_function(probs,target)
loss.backward()
opt.step()
i+=1
#预测
with torch.no_grad():
for inst,label in test_data:
vec=make_bow_vec(inst,word_to_ix)
probs=model(vec)
print(probs,label)
推荐阅读
-
基于pytorch深度学习环境配置
-
TensorFlow实战Google深度学习框架-人工智能教程-自学人工智能的第二天-深度学习
-
机器深度学习二分类电影的情感问题
-
深度学习环境搭建----第二篇常见问题解决
-
机器学习实战学习笔记(二)-KNN算法(2)-使用KNN算法进行手写数字的识别
-
深度学习零基础使用 PyTorch 框架跑 MNIST 数据集的第四天:单例测试
-
pytorch学习(二)variable变量
-
深度学习笔记二:keras+cnn+mnist cnn模型的创建、保存、调用
-
用 pytorch 进行分类(二分类,多分类)
-
深度学习框架Pytorch——学习笔记(六)PyTorch实现L1,L2正则化以及Dropout