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))

f(g(x))=A(Cx+d)+b=ACx+(Ad+b)

AC是一个矩阵, Ad+b是向量,因此我们看到组成仿射图会为您提供仿射图。

从中可以看出,如果您希望神经网络是仿射成分的长链,那么仅制作一个仿射图就不会为模型增加新的功能。

如果我们在仿射层之间引入非线性,则不再是这种情况,我们可以构建功能更强大的模型。

有一些核心非线性。 tanh(x),σ(x),ReLU(x)是最常见的。您可能想知道:“为什么要使用这些功能?我可以想到很多其他非线性因素。” 这是因为它们具有易于计算的梯度,并且计算梯度对于学习至关重要。例如

dσ/dx=σ(x)(1−σ(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) 是

exp(xi)/∑jexp(xj)

应该清楚的是,输出是概率分布:每个元素都是非负的,所有分量的总和为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)

猜你喜欢

转载自blog.csdn.net/u013069552/article/details/112525216
今日推荐