torch-搭建神经网络

以回归和分类为例子进行搭建神经网络

1 回归

先搞几个数据:

a = torch.linspace(-1, 1, 100)  # 返回一个一维张量 起始,终止,生成样本点
x = torch.unsqueeze(a, dim=1)  # dim=0是一行,dim=1是一列,将一个一维tensor变成一个二维的数据的一列(torch不处理一维的数据)
y = x.pow(2) + 0.2*torch.rand(x.size())  # pow是求次幂,rand返回一组按照x的size的随机数 y = x^2+0.2*k,k相对于是个噪点
x, y = Variable(x), Variable(y)
生成的数据

从y的生成可以看出来了,是对y=x^2做一点噪声,然后现在对这个有噪声的数据做一个拟合。

  1. 首先,先搭建神经网络。定义Net类,其中需要继承nn.Module,并实现forward方法。nn.Module包含网络各层的定义及forward方法,继承nn.Module类之后,在构造函数中要调用Module的构造函数,并把网络中具有可学习参数的层放在构造函数。
class Net(nn.Module):
    """
    这是一个最简单的一层的拟合的神经网络。hidden是隐藏层,predict是预测层
    """
    def __init__(self, n_feature, n_hidden, n_output):
        super(Net, self).__init__()  # 继承Net到nn.module

        # 定义层,nn.Linear(in_features,out_features),Linear第一个参数都是层左边输入的特征数,第二个参数是层右边输出的特征数
        self.hidden = nn.Linear(n_feature, n_hidden)  # 一层隐藏层,n_feature个特征,n_hidden个神经元
        self.predict = nn.Linear(n_hidden, n_output)  # 预测层,n_hidden个神经元,n_output个特征的输出
    def forward(self, x):
        x = F.relu(self.hidden(x))  # x经过一个隐藏层,然后再被一个relu函数激活一下
        x = self.predict(x)
        return x

2.第二步,搭建训练步骤,通过反向传播不断调整Net的参数,从而使预测值接近训练数据的标签。

def run_Net():
    net = Net(1, 10, 1)  # 因为x是一维的,所以n_feature=1,神经元设为10个, y是一维的,所以输出为1维,n_output=1
    # print(net)  # 如果想看自己搭的神经元的层结构,直接print就行了
    optimizer = torch.optim.SGD(net.parameters(), lr=0.5)  # 优化器用来训练神经网络,设置为随机梯度下降,lr是学习效率
    loss_func = nn.MSELoss()  # 定义损失函数为均方误差

    for t in range(100):
        prediction = net(x)
        loss = loss_func(prediction, y)  # 计算预测值和真实值的误差,注意预测值在前
        print(loss.data.numpy())
        optimizer.zero_grad()  # zero_grad将net中所有参数的梯度全部降为0(将梯度初始化为0),因为每次计算loss梯度都会保留在net和优化器中
        loss.backward()  # 反向传递,也是一个Variable
        optimizer.step()  # 用优化器优化梯度

最后训练好后,net会存储训练好的参数,再进行输出就可以了。图为:


训练结果

完整代码

import torch
import matplotlib.pyplot as plt
import torch.nn.functional as F
from torch import nn
from torch.autograd import Variable

a = torch.linspace(-1, 1, 100)  # 返回一个一维张量 起始,终止,生成样本点
x = torch.unsqueeze(a, dim=1)  # dim=0是一行,dim=1是一列,将一个一维tensor变成一个二维的数据的一列(torch不处理一维的数据)
y = x.pow(2) + 0.2*torch.rand(x.size())  # pow是求次幂,rand返回一组按照x的size的随机数 y = x^2+0.2*k,k相对于是个噪点
x, y = Variable(x), Variable(y)

# 训练数据示意图
# plt.scatter(x.data.numpy(), y.data.numpy())
# plt.show()

class Net(nn.Module):
    """
    这是一个最简单的一层的拟合的神经网络。hidden是隐藏层,predict是预测层
    """
    def __init__(self, n_feature, n_hidden, n_output):
        super(Net, self).__init__()  # 继承Net到nn.module

        # 定义层,nn.Linear(in_features,out_features),Linear第一个参数都是层左边输入的特征数,第二个参数是层右边输出的特征数
        self.hidden = nn.Linear(n_feature, n_hidden)  # 一层隐藏层,n_feature个特征,n_hidden个神经元
        self.predict = nn.Linear(n_hidden, n_output)  # 预测层,n_hidden个神经元,n_output个特征的输出
    def forward(self, x):
        x = F.relu(self.hidden(x))  # x经过一个隐藏层,然后再被一个relu函数激活一下
        x = self.predict(x)
        return x

def run_Net():
    net = Net(1, 10, 1)  # 因为x是一维的,所以n_feature=1,神经元设为10个, y是一维的,所以输出为1维,n_output=1
    # print(net)  # 如果想看自己搭的神经元的层结构,直接print就行了
    optimizer = torch.optim.SGD(net.parameters(), lr=0.5)  # 优化器用来训练神经网络,设置为随机梯度下降,lr是学习效率
    loss_func = nn.MSELoss()  # 定义损失函数为均方误差

    for t in range(100):
        prediction = net(x)
        loss = loss_func(prediction, y)  # 计算预测值和真实值的误差,注意预测值在前
        print(loss.data.numpy())
        optimizer.zero_grad()  # zero_grad将net中所有参数的梯度全部降为0(将梯度初始化为0),因为每次计算loss梯度都会保留在net和优化器中
        loss.backward()  # 反向传递,也是一个Variable
        optimizer.step()  # 用优化器优化梯度
    return net(x)


if __name__ == '__main__':
    prediction = run_Net()
    
    # 训练结果示意图
    plt.scatter(x.data.numpy(), y.data.numpy())
    plt.plot(x.data.numpy(), prediction.data.numpy(), 'r-')
    plt.show()


2 分类

同样的,先创建一些数据集。比如两个二次分布的数据, 不过他们的均值都不一样。然后用01来区分这两个数据簇。

n_data = torch.ones(100,2)  # n_data
x0 = torch.normal(2*n_data, 1)  # 输入
y0 = torch.zeros(100)  # x0的类别是1
x1 = torch.normal(-2*n_data, 1)
y1 = torch.ones(100)
x = torch.cat((x0, x1), 0).type(torch.FloatTensor)  # 按垂直方向拼接,FloatTensor32位浮点数
y = torch.cat((y0, y1), 0).type(torch.LongTensor)  # LongTensor64位Integer
x, y = Variable(x), Variable(y)

plt.scatter(x.data.numpy()[:, 0], x.data.numpy()[:, 1], c=y.data.numpy(), s=100, lw=0, cmap='RdYlGn')
plt.show()

接下来仍然是定义类:

class Net(nn.Module):
    """
    这是一个最简单的一层的拟合的神经网络。hidden是隐藏层,predict是预测层
    """
    def __init__(self, n_feature, n_hidden, n_output):
        super(Net, self).__init__()  # 继承Net到nn.module

        # 定义层,nn.Linear(in_features,out_features),Linear第一个参数都是层左边输入的特征数,第二个参数是层右边输出的特征数
        self.hidden = nn.Linear(n_feature, n_hidden)  # 一层隐藏层,n_feature个特征,n_hidden个神经元
        self.predict = nn.Linear(n_hidden, n_output)  # 预测层,n_hidden个神经元,n_output个特征的输出

    def forward(self, x):
        x = F.relu(self.hidden(x))  # x经过一个隐藏层,然后再被一个relu函数激活一下
        x = self.predict(x)
        return x

然后进行训练。这里打印出第99次训练的损失函数值,然后再对net的output进行一次计算损失函数值,这两个值是相等的,说明net确实存储了训练好的参数:

def run_Net():
    net = Net(2, 10, 2)  # 第一个参数是输入特征,第二个特征是神经元数,第三个特征是输出的特征(y是两个特征0和1所以输出特征为2)
    optimizer = torch.optim.SGD(net.parameters(), lr=0.2)  # 学习速率慢一点效果更好
    loss_func = nn.CrossEntropyLoss()  # 交叉熵损失函数,判断期望输出与实际输出的接近程度,交叉熵值越小两个的概率分布越接近
    for i in range(100):
        prediction = net(x)
        loss = loss_func(prediction, y)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        if i == 99:
            print(loss)
    print(loss_func(net(x), y))
run_Net()
>> tensor(0.0065, grad_fn=<NllLossBackward>)
>> tensor(0.0065, grad_fn=<NllLossBackward>)

3 快速搭建与保存提取

以上面的回归为例,上面的回归采用的是通过一个class来定义一个Net类。torch中还可以使用其他方法来快速搭建一个神经网络。

# 上面的回归是:net = Net(1, 10, 1)
# 使用Sequential构造一个与net相同构造的net1
net1 = nn.Sequential(
    nn.Linear(1, 10),
    nn.ReLU(),  # 额外升级一下在第一个层后再加一个ReLU
    nn.Linear(10, 1)
)

# 如果想为每层起个名字:
model = nn.Sequential(
    collections.OrderedDict([  # 有序字典  传入字典形式的话就需要是有序字典,得用OrderedDict
              ('hidden layer', nn.Linear(1, 10)),
              ('activating layer', nn.ReLU()),
              ('output layer', nn.Linear(10, 1)),
            ]))

对于训练好的神经网络,,有时候我们想保存它留到下次要用的时候直接提取使用。使用上面的快速搭建出来的net1进行训练并进行保存,torch提供了2种途径来保存,分别为:

def save():
    # 建立一个和上面一样的神经网络,然后保存
    # 如果想看自己搭的神经元的层结构,直接print就行了
    optimizer = torch.optim.SGD(net1.parameters(), lr=0.5)  # 优化器用来训练神经网络,设置为随机梯度下降,lr是学习效率
    loss_func = nn.MSELoss()  # 定义损失函数为均方误差
    for t in range(100):
        prediction = net1(x)
        loss = loss_func(prediction, y)  # 计算预测值和真实值的误差,注意预测值在前
        print(loss.data.numpy())
        optimizer.zero_grad()  # zero_grad将net中所有参数的梯度全部降为0(将梯度初始化为0),因为每次计算loss梯度都会保留在net和优化器中
        loss.backward()  # 反向传递,也是一个Variable
        optimizer.step()  # 用优化器优化梯度
    # torch提供的2种保存方法
    torch.save(net1, 'net.pkl')                      # 保存net的整个计算结果包括计算图
    torch.save(net1.state_dict(), 'net_params.pkl')  # 保留了net的整个参数

对于直接保存计算图和保存模型参数两种保存结果,提取方法如下:

def restore_net():
    # 提取保存的神经网络net
    net2 = torch.load('net.pkl')
    return net2(x)

def restore_params():
    # 新建一个神经网络,然后提取保存的参数net_params
    net3 = Net(1, 10, 1)
    net3.load_state_dict(torch.load('net_params.pkl'))
    return net3(x)

对上面的两种提取方式与直接训练的结果进行对比:

save()
    prediction = run_Net()
    prediction1 = restore_net()
    prediction2 = restore_net()

    plt.subplot(131)
    plt.title('PREDICTION')
    plt.scatter(x.data.numpy(), y.data.numpy())
    plt.plot(x.data.numpy(), prediction.data.numpy(), 'r-', lw=5)

    plt.subplot(132)
    plt.title('PREDICTION1')
    plt.scatter(x.data.numpy(), y.data.numpy())
    plt.plot(x.data.numpy(), prediction1.data.numpy(), 'r-', lw=5)

    plt.subplot(133)
    plt.title('PREDICTION2')
    plt.scatter(x.data.numpy(), y.data.numpy())
    plt.plot(x.data.numpy(), prediction2.data.numpy(), 'r-', lw=5)
    plt.show()

得到结果如图,可以看出结果基本是一致的,当然也存在不一致的情况,因为毕竟输出的时候又走了一遍神经网络。


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

推荐阅读更多精彩内容