《动手学习深度学习》第二天

正式开始啦!!!

线性回归

1、回归与分类的差别

最开始接触机器学习的时候,回归与分类就困扰了好久,就像书中说的那样回归的输出是连续的,就像天气的变化,在一定时间内都是连续变化的,不会说一下子温度就会从20度到30度,并且只要单位够小,从20度到21度之间可以划分为无数个不同度数。同样拿天气来说,分类预测明天是否会下雨,我们会提前给定几个状态,下雨还是不下雨,又或者说阴天,就算加上什么雷阵雨的,也是可数的几个状态。

2、线性回归原理

书中的介绍已经够清楚了,就是通过线性模型来找到输入与输出之前的关系。这里还是以房价为例,并且假设房价与面积,房龄之间是线性关系。
想要建立一个模型有以下几个步骤:
(1)收集数据
(2)构建模型,也就是通过线性表达式表示出输入与输出之间的关系:
在这里插入图片描述
(3)损失函数,也就是评价预测与真实值之间的指标的函数。
在这里插入图片描述

(4)优化算法,这里使用小批量随机梯度下降法
在这里插入图片描述
(5)模型预测,将求得的参数代入公式就可以啦
书中大概介绍了为什么我们一般拿到的数据集都是矩阵或者数组形式的,简单直接地用运算时间给出了结果,因为这样运算速度快。

3、线性回归实现

书中给出了用Mxnet框架的详细代码,这里只是用pytorch照着写一遍,但就是照着写一遍我还是花了整整一天,因为在计算梯度这一块一直报错,头大。
(1)首先导包


```python
import matplotlib.pyplot as plt
%matplotlib inline
import torch
from torch.autograd import Variable
import numpy as np
import random
from IPython import display```

(2)初始化数据
这里设训练数据集样本数为1000,输⼊个数(特征数)为2。给定随机⽣成的批量样本1000×2,我们使⽤线性回归模型真实权重w = [2, −3.4]⊤和偏差b = 4.2,以及⼀个随机噪声项ϵ来⽣成标签,和书中一模一样。

num_inputs = 2
num_examples = 1000
true_w = [2,-3.4]
true_b = 4.2
features = torch.tensor(np.random.normal(scale = 1,size = (num_examples,num_inputs)),dtype=torch.float32)
labels = true_w[0] * features[:,0] + true_w[1] * features[:,1] + true_b
labels += torch.tensor(np.random.normal(scale = 0.01,size = labels.shape),dtype=torch.float32)

# 随机生成w和b
w = torch.tensor(np.random.normal(scale=0.01,size=(num_inputs,1)),dtype=torch.float32,requires_grad = True)
b = torch.zeros(1,requires_grad = True,dtype=torch.float32)

(3)画图
这里就是大概看一下某一维度特征和标签之间的关系,对于不清楚分布的情况的数据来说,可以大概推测一个合适的模型进行建模。

def use_svg_display():
#     用矢量图显示
    display.set_matplotlib_formats('svg')
def set_figsize(figsize = (3.5,2.5)):
    use_svg_display()
#     设置图的尺寸
    plt.rcParams['figure.figsize'] = figsize
set_figsize()
plt.scatter(features[:,1].data,labels.data,1)

在这里插入图片描述

(4)定义模型
这里把批量读取数据的函数和目标函数,损失函数,优化器函数的定义都放在这里了,对应着上面的公式就可以看明白。

# 批量读取数据
def data_iter(batch_size,features,labels):
    num_examples = len(features)
    indices = list(range(num_examples))
    random.shuffle(indices)
    for i in range(0,num_examples,batch_size):
        j = torch.LongTensor(indices[i:min(i + batch_size,num_examples)])
        yield features[j,:],labels[j]
# 定义线性模型
def Linreg(X,w,b):
    return torch.mm(X,w) + b
# 定义损失函数
def squared_loss(y_hat,y):
    return (y_hat - y.reshape(y_hat.shape))**2/2
# 定义sgd优化模型
def sgd(params,lr,batch_size):
    for param in params:
#         param[:] = param - lr * param.grad/ batch_size
        param.data -= lr * param.grad/batch_size

(5)准备训练

# 初始化训练参数
lr = 0.01
num_epochs = 10
batch_size = 10
net = Linreg
loss = squared_loss

# 开始训练
for epoch in range(num_epochs):
    for X,y in data_iter(batch_size,features,labels):
        if (w.grad is not None) and (b.grad is not None):
            w.grad.data.zero_()
            b.grad.data.zero_()  
        l = loss(net(X,w,b),y).sum()
        l.backward()
        sgd([w,b],lr,batch_size)
    train_l = loss(net(features,w,b),labels)
    print('epoch %d,loss %f,'%(epoch+1,train_l.mean()))

(6)训练结果
在这里插入图片描述
模型比较简单,很快就能把损失降下来,接下来就是记录下我疯狂报错的一下午。
(7)错误示范
1、报错
在这里插入图片描述
报了好几个错误,都是关于无法进行反向传播的,之前也用过pytorch的代码,也没有觉得在需不需要进行反向传播这一块这么繁琐,主要是自己水平也不够,就开始了各种调试,既然是没法传播的问题,想了下我就把涉及计算的X,y,w,和b都修改为了需要计算梯度的,很明显,并没有什么用,原因在成功解决问题后,推测可能是因为X,y根本没有计算反向传播的必要啊,假设y = wx + b,一个函数中不可能4个未知数吧,X,y已知,反向传播往哪里传。随后,又去看了以前使用的pytorch代码,对比了好久没有发现什么差异啊,数据都是Tensor形式的,差别就在于现在pytorch中都是封装好了优化函数的,而在这本书中,为了使我们更加了解深度学习的原理,自己的定义的优化函数SGD随机梯度下降。然后,果然,怀疑别人出错前应该先检查自己,问题就在自己写的随机梯度下降这个1行代码中,简直要吐血。
在这里插入图片描述
其中注释掉的那一行代码就是我原本写的,错的代码,首先要说一下在y = wx + b中,w,b是未知数,所以要进行反向传播的就是这两个参数,所以在写代码时,就会把w和b的requires_grad设置为True,然后运行到SGD时,错的那行代码会在进行梯度优化时进行梯度计算,目前为什么优化时不需要进行梯度计算,我还不知道,但是明确了就是这一行代码的错误,修改成下面的,不需要对优化过程进行梯度计算就可以了。好难,还在晕。

2、心得体会
我一直不太明白构建计算图,与梯度计算之间的关系,在代码报错后去看了好久还是看的晕乎乎,大概就是再说使用pytorch进行反向传播的时候,一定要注意哪些参数在整个代码中是需要进行反向传播的,只有需要的才要将requires_grad设置为True。学习到这里,整个人都超负荷了,这个周都不想在学习了。。。。。。。。

3、疑惑
今天又看了下书中用Mxnet框架的写法和讨论区大家的疑惑,然而我的问题还是没有解决反而更加疑惑了,为什么在Mxnet框架中在使用优化算法对w和b进行更新时,不需要开辟新的空间,而pytorch需要呢,是因为框架结构不一样吗,还是我理解错了,先把问题放在这里,等过后了解更多了再回过头来解决。

4、线性回归简洁实现

都是线性回归的相关操作,所以就放在一起了。简洁实现顾名思义就是用pytorch封装好的函数实现某些功能。那就直接上代码啦。
(1)数据初始化
还是和上面一摸一样的数据

import matplotlib.pyplot as plt
%matplotlib inline
import torch
from torch.autograd import Variable
import numpy as np
import random
from IPython import display

num_inputs = 2
num_examples = 1000
true_w = [2,-3.4]
true_b = 4.2
features = torch.tensor(np.random.normal(scale = 1,size = (num_examples,num_inputs)),dtype=torch.float32,)
labels = true_w[0] * features[:,0] + true_w[1] * features[:,1] + true_b
labels += torch.tensor(np.random.normal(scale = 0.01,size = labels.shape),dtype=torch.float32,)

(2)数据读取
之前我们是自己定义的批量读取数据的函数,但是在pytorch中已经定义好了相关的类,可以直接生成对象后使用。

batch_size = 10
from torch.utils.data import Dataset,DataLoader,TensorDataset
dataset = TensorDataset(features,labels)
data_iter = DataLoader(dataset,batch_size,shuffle=True)
for X,y in data_iter:
    print(X,y)
    break

(3)模型定义
pytorch定义模型的方式有几种,这是比较通用,且方便后续修改的一种方式。
1、方法一

from torch import nn
class Lineraregssion(nn.Module):
    def __init__(self):
        super(Lineraregssion,self).__init__()
        self.linear = nn.Linear(2,1)
    
    def forward(self,X):
        out = self.linear(X)
        return out
lin = Lineraregssion()   
print(lin)

在这里插入图片描述

之前也遇到过许多这样写的代码,但是在自己写的时候,还是会出现一些莫名其妙的错误,例如我在上面实例化了Linear这个类,然后再前向传播中传入x就可以了,但是在自己写的时候,竟然传入的时X,和y,哎,学习太难了。。。。。
2、方法二

net = nn.Sequential()
net.add_module('linear',nn.Linear(2,1))
print(net)

在这里插入图片描述
不知道是不是我级别太低了,我觉得这种构建方式比较简单好用,适合我这种初学者。
3、方式三

net = nn.Sequential(
    nn.Linear(2,1)
)
print(net)

在这里插入图片描述

还可以直接在Sequential里面添加,与上面那种方式的差别就是多了名字。

for param in net.parameters():
    print(param)

在这里插入图片描述
通过这样的循环可以查看中间到底存在哪些参数。
(4)模型参数初始化

from torch.nn import init
init.normal_(net[0].weight,mean=0,std=0.01)
init.constant_(net[0].bias,val=0)

在pytorch模型中,要是忘记初始化参数了,应该会自动初始化的,因为我就一直都没有对参数进行初始化,不影响训练。

(5)定义损失函数和优化算法

cetrion = torch.nn.MSELoss()
opt = torch.optim.SGD(lin.parameters(),0.01)

(6)训练模型

num_epoch = 10
for epoch in range(1,num_epoch + 1):
    for X,y in data_iter:
        out = lin(X)
#         print(out.shape)
        l = cetrion(out,y.view(out.shape))
        opt.zero_grad()
        l.backward()
        opt.step()
    print('epoch: %d, loss:%f'%(epoch,l.item()))

然后可以通过取出模型中的参数查看w和b的数值

dense = lin.linear
dense.weight
dense.bias

(7)心得体会
在这里主要比之前多了解了一个非常重要的东西那就是参数的初始化,因为之前训练resnet这类模型的时候都是加载别人训练好的权重进行训练的,就在想难道非要用别人训练的权重吗,虽然别人预训练的效果是比较不错,但是如果自己要修改代码怎么办呢?现在就知道了可以通过直接调用nn里面的init对权重进行初始化,以满足自己的需求。如果要对代码的不同部分使用不同的学习率就可以这样设置
在这里插入图片描述
这里模型中就只用了一个线性层,就不进行试验啦。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值