训练一个模型的过程

1.基本概念

梯度下降(gradient descent):计算损失函数关于参数的导数(也称梯度),如下图更新参数。

image.png

batch:计算损失函数最简单的做法是针对数据集中所有样本的损失均值求关于参数的导数,再进行参数更新;但遍历整个数据集使实际中的执行可能会非常慢。因此最常用的用法是随机抽取一小批样本batch_size即这个小批量的训练样本数量,计算小批量的平均损失关于模型参数的导数,再进行更新。
epoch: 在每个迭代周期(epoch)中,遍历整个数据集, 并将训练数据集中所有样本都使用一次。num_epochs是超参数,自己设定。

2.训练过程

设y = wx+b

2.1定义data_iter来读取数据集

定义一个data_iter函数
输入:batch_size、features、labels
输出:生成大小为batch_size的小批量(每一个为feature和对应的label)
纯手写

def data_iter(batch_size, features, labels):
    num_examples = len(features) #样本数量
    indices = list(range(num_examples)) #取batch的下标
    # 这些样本是随机读取的,没有特定的顺序
    random.shuffle(indices) #通过shuffle打乱下标
    for i in range(0, num_examples, batch_size):
        batch_indices = torch.tensor(
            indices[i: min(i + batch_size, num_examples)])
        yield features[batch_indices], labels[batch_indices]

使用torch
torch.utils.data包含相关处理接口

from torch.utils import data
def load_array(data_arrays, batch_size, is_train=True):  #
    """构造一个PyTorch数据迭代器"""
    dataset = data.TensorDataset(*data_arrays)  #包装数据
    return data.DataLoader(dataset, batch_size, shuffle=is_train) #用dataloader进行封装
batch_size = 10
data_iter = load_array((features, labels), batch_size)

2.2定义模型

假设定义为线性模型

def linreg(X, w, b):  
    """线性回归模型"""
    return torch.matmul(X, w) + b

使用torch
简单的模型,直接使用torch.nn的接口,模型1如下:

from torch import nn
net = nn.Sequential(nn.Linear(2, 1))

复杂的模型,可继承 nn.Module(它本身是一个类并且能够跟踪状态)建立子类,并实例化模型,模型2如下是一个基于bert的多标签模型,其中也用到了nn.Linear()建立线性层:

class Model(nn.Module):
    def __init__(self, config):
        super(Model, self).__init__()   #调用父类方法
        self.bert = BertModel.from_pretrained(config.bert_path)
        for param in self.bert.parameters():
            param.requires_grad = True #True代表需要forward train ,false则参数固定不微调
        self.fc = nn.Linear(config.hidden_size, config.num_classes)
        self.drop = nn.Dropout(config.dropout)
    def forward(self, x):
        #x[ids,seq_len,mask]
        context = x[0]  # 输入的句子 shape[batch_size,pad_size]
        mask = x[2]  # 对padding部分进行mask,和句子一个size,padding部分用0表示,如:[1, 1, 1, 1, 0, 0]
        _, pooled = self.bert(context, attention_mask=mask, output_all_encoded_layers=False) #pooled shape[batch_size,hidden_size]
        out = self.fc(pooled) #shape[batch_size,num_classes]
        out = torch.sigmoid(out)
        return out

2.3初始化模型参数

纯手写

w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
b = torch.zeros(1, requires_grad=True) 

使用torch
正如构造nn.Linear时可指定输入和输出尺寸, 也能直接访问参数以设定它们的初始值。 通过net[0]选择网络中的第一个图层, 然后使用weight.data和bias.data方法访问参数。 还可以使用替换方法normal_和fill_来重写参数值。
针对模型1

net[0].weight.data.normal_(0, 0.01)
net[0].bias.data.fill_(0)

2.4定义loss function

纯手写
假设定义为均方误差

def squared_loss(y_hat, y):  #
    """均方损失"""
    return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2

使用torch
torch.nn和torch.nn.functional中都定义了多种loss functions(交叉熵、均方差等)、activate functions,还定义了Convolution functions、Pooling functions等,可直接调用loss functions接口。但torch.nn和torch.nn.functional使用方法略有不同
官网地址:https://pytorch.org/docs/stable/nn.html
torch.nn调用代码实例:

loss = nn.MSELoss() #定义loss function
l = loss(net(X) ,y) #计算

torch.nn.functional调用代码实例:

import torch.nn.functional as F
loss = F.mse_loss(net(X) ,y)  # 不用定义,直接计算mse_loss

2.5定义优化算法

纯手写

def sgd(params, lr, batch_size):  
    """小批量随机梯度下降"""
    with torch.no_grad():
    #遍历params数组,对里面的每一个参数进行更新
        for param in params:
            param -= lr * param.grad / batch_size
            param.grad.zero_()

使用torch
直接调用接口,调用代码如下:

from torch import optim
trainer =optim.SGD(net.parameters(), lr=0.03)

2.6训练

纯手写

lr = 0.03
num_epochs = 3
net = linreg
loss = squared_loss

for epoch in range(num_epochs):
    for X, y in data_iter(batch_size, features, labels):
        l = loss(net(X, w, b), y)  # X和y的小批量损失
        # 因为l形状是(batch_size,1),而不是一个标量。l中的所有元素被加到一起,
        # 并以此计算关于[w,b]的梯度
        l.sum().backward()
        sgd([w, b], lr, batch_size)  # 使用参数的梯度更新参数
    with torch.no_grad():
        train_l = loss(net(features, w, b), labels)
        print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')

使用torch

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

推荐阅读更多精彩内容