使用 PyTorch 进行简单的回归与分类

作者: 思想永不平凡 | 来源:发表于2020-01-11 16:55 被阅读0次

    最近正在学 PyTorch,作为一个初学者,在此分享下自己所学到的一些皮毛吧。



    这里不对神经网络的概念作太多的介绍,重点是如何使用 Pytorch 搭建一个简单的神经网络来作一些简单的回归与分类。

    神经网络简单介绍

    这是一个包含三个层次的神经网络。红色的是输入层,紫色的是中间层(也叫隐藏层),绿色的是输出层。
    输入层有3个输入单元,隐藏层有4个单元,输出层有2个单元。

    image.png

    这里就不对神经网络作太多的介绍,网上有很多很好的博客,大家可以去查阅查阅。

    使用 PyTorch 进行简单的回归

    你所需要安装的 python 库是 pytorch 和 matplotlib。如果你正确安装了这两个库,并且使用的是python3,那么理论上你就可以使用它了,两个程序参考了 github 上一位莫烦大神的教程(https://github.com/MorvanZhou
    ),我对其做了一些“魔改”,简单封装了下,使其更容易“调参”。

    激励函数使用的是 relu


    image.png

    以上是常见的激励函数,具体什么场景使用怎么的激励函数这里就不作赘述了。
    程序里有详细的注释,还是见程序吧:

    import torch
    from torch.autograd import Variable
    import matplotlib.pyplot as plt
    import matplotlib.animation as animation
    
    
    class MineNet(torch.nn.Module):
        def __init__(self, _net):
            super(MineNet, self).__init__()
            self.n_feature = _net[0]
            self.n_hidden = _net[1]
            self.n_output = _net[2]
            """torch.nn.Linear(self, in_features, out_features, bias=True)
            in_features : 前一层网络神经元的个数
            out_features : 该网络层神经元的个数
            """
            '''隐藏层线性输出'''
            self.hidden = torch.nn.Linear(self.n_feature, self.n_hidden)
            '''输出层线性输出'''
            self.predict = torch.nn.Linear(self.n_hidden, self.n_output)
    
        def forward(self, values):
            """
            正向传播输入值, 神经网络分析出输出值
            :param values:
            :return:  输出值
            """
            '''激励函数(隐藏层的线性值)'''
            '''relu: x<=0 y=0;x>0 y=x'''
            values = torch.relu(self.hidden(values))
            return self.predict(values)
    
    
    class Net(object):
        def __init__(self, x, y, count, lr, mine_net):
            """
            :param x: 自变量
            :param y: 因变量
            :param count: 训练次数
            :param lr: 学习效率
            :param mine_net: MineNet对象
            """
            self.x = Variable(x)
            self.y = Variable(y)
            self.count = count
            self.lr = lr
            self.net = mine_net
            '''net 的所有参数,学习率'''
            self.optimizer = torch.optim.SGD(self.net.parameters(), lr=self.lr)
            '''预测值和真实值的误差计算公式 (均方差)'''
            self.loss_fun = torch.nn.MSELoss()
    
        def train_show(self):
            """
            训练与可视化
            :return:
            """
            plt.ion()
            plt.show()
            for t in range(self.count):
                '''给net训练数据, 输出预测值'''
                prediction = self.net(self.x)
                '''计算两者的误差'''
                loss = self.loss_fun(prediction, self.y)
                '''清空上一步的残余更新参数值'''
                self.optimizer.zero_grad()
                '''误差反向传播, 计算参数更新值'''
                loss.backward()
                '''将参数更新值施加到net的parameters上'''
                self.optimizer.step()
                '''作图'''
                plt.cla()
                plt.scatter(self.x.data.numpy(), self.y.data.numpy())
                plt.plot(self.x.data.numpy(), prediction.data.numpy(), 'r-', lw=5)
                plt.text(0.5, 0, 'Count =%.d\nLoss=%.4f' % (t + 1, loss.data.numpy()),
                         fontdict={'size': 14, 'color': 'red'})
                plt.pause(0.1)
    
            plt.ioff()
            plt.show()
    
    
    if __name__ == '__main__':
        '''自变量'''
        _x = torch.unsqueeze(torch.linspace(-1, 1, 100), dim=1)
        '''因变量'''
        _y = _x.pow(2) + 0.2 * torch.rand(_x.size())
        '''学习次数'''
        c = 300
        '''学习效率'''
        _lr = 0.5
        net_list = [1, 10, 1]
        net = MineNet(net_list)
        n = Net(_x, _y, c, _lr, net)
        n.train_show()
    
    
    

    呃,由于我暂时还不太会把这个过程保存为动图,这里暂时把过程的结果展示下,之后再放动图。
    结果如下:


    image.png

    注:Count 是学习次数,Loss 是误差。

    使用 Pytorch 进行简单的分类

    分类在回归的基础上作一些修改就可以了。
    程序中有详细的注释:

    import torch
    import matplotlib.pyplot as plt
    import torch.nn.functional as F
    
    
    class MineNet(torch.nn.Module):
        def __init__(self, _net):
            super(MineNet, self).__init__()
            self.n_feature = _net[0]
            self.n_hidden = _net[1]
            self.n_output = _net[2]
            """torch.nn.Linear(self, in_features, out_features, bias=True)
            in_features : 前一层网络神经元的个数
            out_features : 该网络层神经元的个数
            """
            '''隐藏层线性输出'''
            self.hidden = torch.nn.Linear(self.n_feature, self.n_hidden)
            '''输出层线性输出'''
            self.output = torch.nn.Linear(self.n_hidden, self.n_output)
    
        def forward(self, values):
            """
            正向传播输入值, 神经网络分析出输出值
            :param values:
            :return:  输出值
            """
            '''激励函数(隐藏层的线性值)'''
            '''relu: x<=0 y=0;x>0 y=x'''
            values = F.relu(self.hidden(values))
            return self.output(values)
    
    
    class Net(object):
        def __init__(self, x, y, count, lr, mine_net):
            """
            :param x: 自变量
            :param y: 因变量
            :param count: 训练次数
            :param lr: 学习效率
            :param mine_net: MineNet对象
            """
            self.x = torch.cat((x[0], x[1]), 0).type(torch.FloatTensor)  # FloatTensor = 32-bit floating
            self.y = torch.cat((y[0], y[1]), ).type(torch.LongTensor)  # LongTensor = 64-bit integer
            self.count = count
            self.lr = lr
            self.net = mine_net
            '''net 的所有参数,学习率'''
            self.optimizer = torch.optim.SGD(self.net.parameters(), lr=self.lr)
            '''预测值和真实值的误差计算公式 (均方差)'''
            self.loss_fun = torch.nn.CrossEntropyLoss()
    
        def train_show(self):
            """
            训练与可视化
            :return:
            """
            plt.ion()
            plt.show()
            for t in range(self.count):
                '''给net训练数据, 输出预测值'''
                out = self.net(self.x)
                '''计算两者的误差'''
                loss = self.loss_fun(out, self.y)
                '''清空上一步的残余更新参数值'''
                self.optimizer.zero_grad()
                '''误差反向传播, 计算参数更新值'''
                loss.backward()
                '''将参数更新值施加到net的parameters上'''
                self.optimizer.step()
                '''作图'''
                plt.cla()
                '''经过 softmax 的激励函数后的最大概率才是预测值'''
                prediction = torch.max(F.softmax(out, dim=1), 1)[1]
                predict_y = prediction.data.numpy().squeeze()
                target_y = self.y.data.numpy()
                plt.scatter(self.x.data.numpy()[:, 0], self.x.data.numpy()[:, 1], c=predict_y, s=100, lw=0, cmap='RdYlGn')
                ''''预测中有多少和真实值一样'''
                accuracy = sum(predict_y == target_y) / 200.
                plt.text(1.5, -4, 'Count =%.d\nAccuracy=%.2f' % (t + 1, accuracy), fontdict={'size': 14, 'color': 'red'})
                plt.pause(0.1)
    
            plt.ioff()
            plt.show()
    
    
    if __name__ == '__main__':
        n = torch.ones(100, 2)
        '''自变量'''
        _x = [torch.normal(2 * n, 1), torch.normal(-2 * n, 1)]
        '''因变量'''
        _y = [torch.zeros(100), torch.ones(100)]
        '''学习次数'''
        c = 60
        '''学习效率'''
        _lr = 0.02
        net_list = [2, 10, 2]
        net = MineNet(net_list)
        n = Net(_x, _y, c, _lr, net)
        n.train_show()
    
    
    
    

    结果如下:


    image.png

    注:Count 是学习次数,Accuracy 是准确率。

    可以在 if name == 'main': 中调节部分参数。
    就本人的使用来看,鉴于隐藏层和输出层用的是 torch.nn.Linear,如果在作回归时,x 与 y 是多项式关系,拟合效果还不错,但是对于其他关系,效果可能就不太好了,当然你也可以调节其他参数,比如学习次数,学习效率等等,得到的结果每次都会有差别。

    相关文章

      网友评论

        本文标题:使用 PyTorch 进行简单的回归与分类

        本文链接:https://www.haomeiwen.com/subject/iafbactx.html