Pytorch:一个简单的神经网络——分类

原文地址

分类目录——Pytorch

生成训练数据

# 生成训练数据
n_data = torch.ones(100, 2)  # 数据的基本形态,全1矩阵,shape=(100,2)
x0 = torch.normal(2 * n_data, 1)  # 类型0 x data (tensor), shape=(100, 2)
# print(torch.normal(2*n_data, 1))
# normal()传的两个值为均值和标准差,2*n_data就成了一个全是2的矩阵
# normal()出的矩阵中的数据满足以2为均值,以1为标准差的正则分布(就是大部分数据都离2很近,少量数据离2远)
y0 = torch.zeros(100)  # 类型0 y data (tensor), shape=(100, )
x1 = torch.normal(-2 * n_data, 1)  # 类型1 x data (tensor), shape=(100, 1)
# normal()出的矩阵中的数据满足以-2为均值,以1为标准差的正则分布
y1 = torch.ones(100)  # 类型1 y data (tensor), shape=(100, )

观察一下生成的数据

1581392200394

构造并生成网络(通过定义类的方式)

class Net(torch.nn.Module):  # 继承 torch 的 Module
    # 我理解为这是在声明网络的静态结构
    def __init__(self, n_feature, n_hidden, n_output):
        super(Net, self).__init__()  # 继承 __init__ 功能
        # 定义每层用什么样的形式
        # .Liner(),“直”(线性)部分,相当与y=f(Wx)中Wx
        self.hidden = torch.nn.Linear(n_feature, n_hidden)  # 隐藏层线性输出
        self.predict = torch.nn.Linear(n_hidden, n_output)  # 输出层线性输出
        # 上面两句就是声明了Net有两层,hidden和predict
        # 如果有多层可以self.hidden1,self.hidden2...,应该注意前尾部与后首的连贯,就是上面两句的n_hidden参数

    # 我理解为声明网络的处理流程,有动态的意思
    def forward(self, x):  # 这同时也是 Module 中的 forward 功能
        # 正向传播输入值, 神经网络分析出输出值
        # 激活函数激活,“弯”非线性部分,相当于y=f(Wx)中的f()部分
        x = F.relu(self.hidden(x))  # 激励函数(隐藏层的线性值)
        # 这一句可以理解为x1=f(x0)
        # y = self.predict(x)  # 输出值
        y = F.softmax(self.predict(x), dim=-1)  # 输出值
        # softmax()是分类中常用的激活函数,它可以将输出转成表达当前数据在每一类上的概率,eg[0.1, 0.9]
        # dim   0 表示对列进行softmax; -1 表示对行进行softmax
        return y


# 声明一个网络
net = Net(n_feature=2, n_hidden=10, n_output=2)
# 数据有两个特征,即点的横纵两个坐标
# 我们(最开始)输入的y是1维的,但是它的(最终)输出是2维的 tensor (p_class0, p_class1)

可以通过print()直接打印生成的网络

# 通过print可以直接打印网络的结构
# print(net)  # net 的结构
# Net(
#   (hidden): Linear(in_features=2, out_features=10, bias=True)
#   (predict): Linear(in_features=10, out_features=2, bias=True)
# )

配置网络并进行训练

# optimizer 是训练的工具
optimizer = torch.optim.SGD(net.parameters(), lr=0.02)
# 传入 net 的所有参数, 学习率
loss_func = torch.nn.CrossEntropyLoss()
# 误差的计算方式,这里选用的交叉熵损失
# 交叉熵损失是分类中常用的一种损失函数,表示数据的不确定程度,大概可以这么理解,越合理的分类结果(视觉上就是成堆聚在一块的分成一类),其交叉熵值越小,反之,则越大

for t in range(100):
    res = net(x)  # 喂给 net 训练数据 x, 输出分析值

    # if t==90:
    #     print(res)

    loss = loss_func(res, y)  # 计算两者的误差

    optimizer.zero_grad()  # 清空上一步的残余更新参数值
    loss.backward()  # 误差反向传播, 计算参数更新值
    optimizer.step()  # 将参数更新值施加到 net 的 parameters 上

训练过程可视化(代码在文尾全部代码部分给出)

训练过程中

1581393200434

训练收敛之后

1581393220752

可以看到准确率并没有达到100%,这个可以看一下起初生成的数据,会发现有些介于两类交界处的点,有些异常点,看起来离这一类很近,却属于另一个类。这些点可以理解为噪声数据。

为了更好的理解网络的输出,我把其中一轮训练的结果进行了print

    if t==90:
    	print(res)

数据太多,这里只截取中间一部分进行观察

1581393537526

全部代码

import torch
import torch.nn.functional as F     # 激励函数都在这
import matplotlib.pyplot as plt

torch.manual_seed(0)    # 为了使每次随机生成的数据都是一样的

# 生成训练数据
n_data = torch.ones(100, 2)  # 数据的基本形态,全1矩阵,shape=(100,2)
x0 = torch.normal(2 * n_data, 1)  # 类型0 x data (tensor), shape=(100, 2)
# print(torch.normal(2*n_data, 1))
# normal()传的两个值为均值和标准差,2*n_data就成了一个全是2的矩阵
# normal()出的矩阵中的数据满足以2为均值,以1为标准差的正则分布(就是大部分数据都离2很近,少量数据离2远)
y0 = torch.zeros(100)  # 类型0 y data (tensor), shape=(100, )
x1 = torch.normal(-2 * n_data, 1)  # 类型1 x data (tensor), shape=(100, 1)
# normal()出的矩阵中的数据满足以-2为均值,以1为标准差的正则分布
y1 = torch.ones(100)  # 类型1 y data (tensor), shape=(100, )

# 注意 x, y 数据的数据形式是一定要像下面一样 (torch.cat 是在合并数据)
x = torch.cat((x0, x1), 0)
y = torch.cat((y0, y1), 0).type(torch.long)
# 为y改变一下数据类型,因为网络的输出结果是long类型,在计算loss时需要匹配

# # 观察一下生成的数据
# plt.scatter(x0[:, 0], x0[:, 1], color='red')
# plt.scatter(x1[:, 0], x1[:, 1], color='green')
# plt.show()


class Net(torch.nn.Module):  # 继承 torch 的 Module
    # 我理解为这是在声明网络的静态结构
    def __init__(self, n_feature, n_hidden, n_output):
        super(Net, self).__init__()  # 继承 __init__ 功能
        # 定义每层用什么样的形式
        # .Liner(),“直”(线性)部分,相当与y=f(Wx)中Wx
        self.hidden = torch.nn.Linear(n_feature, n_hidden)  # 隐藏层线性输出
        self.predict = torch.nn.Linear(n_hidden, n_output)  # 输出层线性输出
        # 上面两句就是声明了Net有两层,hidden和predict
        # 如果有多层可以self.hidden1,self.hidden2...,应该注意前尾部与后首的连贯,就是上面两句的n_hidden参数

    # 我理解为声明网络的处理流程,有动态的意思
    def forward(self, x):  # 这同时也是 Module 中的 forward 功能
        # 正向传播输入值, 神经网络分析出输出值
        # 激活函数激活,“弯”非线性部分,相当于y=f(Wx)中的f()部分
        x = F.relu(self.hidden(x))  # 激励函数(隐藏层的线性值)
        # 这一句可以理解为x1=f(x0)
        # y = self.predict(x)  # 输出值
        y = F.softmax(self.predict(x))  # 输出值
        # softmax()是分类中常用的激活函数,它可以将输出转成表达当前数据在每一类上的概率,eg[0.1, 0.9]
        return y


# 声明一个网络
net = Net(n_feature=2, n_hidden=10, n_output=2)
# 数据有两个特征,即点的横纵两个坐标
# 我们(最开始)输入的y是1维的,但是它的(最终)输出是2维的 tensor (p_class0, p_class1)

# 通过print可以直接打印网络的结构
# print(net)  # net 的结构
# Net(
#   (hidden): Linear(in_features=2, out_features=10, bias=True)
#   (predict): Linear(in_features=10, out_features=2, bias=True)
# )

# optimizer 是训练的工具
optimizer = torch.optim.SGD(net.parameters(), lr=0.02)
# 传入 net 的所有参数, 学习率
loss_func = torch.nn.CrossEntropyLoss()
# 误差的计算方式,这里选用的交叉熵损失
# 交叉熵损失是分类中常用的一种损失函数,表示数据的不确定程度,大概可以这么理解,越合理的分类结果(视觉上就是成堆聚在一块的分成一类),其交叉熵值越小,反之,则越大

plt.ion()   # 画图
plt.show()

for t in range(100):
    res = net(x)  # 喂给 net 训练数据 x, 输出分析值

    # if t==90:
    #     print(res)

    loss = loss_func(res, y)  # 计算两者的误差

    optimizer.zero_grad()  # 清空上一步的残余更新参数值
    loss.backward()  # 误差反向传播, 计算参数更新值
    optimizer.step()  # 将参数更新值施加到 net 的 parameters 上

    # 接着上面来
    if t % 2 == 0:
        plt.cla()
        # 过了一道 softmax 的激励函数后的最大概率才是预测值
        prediction = torch.max(res, 1)[1]
        plt.scatter(x[:, 0], x[:, 1], c=prediction, lw=0, cmap='RdYlGn')
        accuracy = sum((prediction == y).numpy()) / 200.  # 预测中有多少和真实值一样
        plt.text(1.5, -4, 'Accuracy=%.2f' % accuracy, fontdict={'size': 20, 'color': 'red'})
        plt.pause(0.1)

plt.ioff()  # 停止画图
plt.show()

参考文献

代码主要来自 区分类型(分类) ,进行了注释,略有改动

发布了102 篇原创文章 · 获赞 68 · 访问量 5109

猜你喜欢

转载自blog.csdn.net/BBJG_001/article/details/104274192