PyTorch-02梯度下降Gradient Descent、回归案例、手写数字识别案例

PyTorch-02梯度下降Gradient Descent、回归案例、手写数字识别案例

了解梯度下降

梯度下降是深度学习的精髓。整个deep learning是靠梯度下降所支撑的。可以求解一个非常难的函数,使用的方法就是梯度下降算法。

求一个函数的极小值,就可以先求其函数对应的导数,再检验这个函数的导数是否为极大值或者极小值点。梯度下降与上述的方法类似,但这里还需要一个迭代计算的过程。
例子:
函数y=x^2·sin(x)
该函数导数y’=2·x·sin(x) + x^2·cos(x)
在每获得一个倒数的时候,在x基础上减去y函数在x处的导数。即x’=x-∂x这样就可以获得一个新的x。
比如说,该函数在某一处的x为2.5,其函数y在x为2.5处时的导数为-0.9,新的x=2.5-0.9,但是这里如果之间减去-0.9,会让新的x变化过快,所以这里需要给一个缩放倍数(即学习的速率)使得新x调整的过程不会过大。
所以最终新的 x’ = x - ∂x·learningRate,这样新的x变化情况就可以变化的非常非常小。
假设x=5时,learningRate = 0.005,∂x=0,该函数y有最小值,即x’=5 - 0·0.005 = 5 ,这样x’依然为5,实际上∂x不会是0,会有一定的计算误差,x’会在5附加产生一定的抖动。
在这里插入图片描述

梯度下降思想在线性回归情况中的应用:

理想情况:精确求解Closed Form Solution

在这里插入图片描述
对于精确求解很多实际情况下是做不到的,但是如果可以达到近似求解并且在实际情况下被证明可行,这样就已经满足我们的目的了,不需要一个非常准确的精确求解Closed Form Solution。

真实情况:伴随着噪声

在这里插入图片描述
这里我们需要求解的是y = w·x + b 这个函数,与真实y之间的差值,即wx + b 与y真实值之间的差值和最小。这里表示均方误差最小。loss = (WX+b-y)^2的最小值。使得y真实与wx+b之间更加接近。

案例:
在这里插入图片描述
对于具体的方程:loss = ∑i (w·xi+b-yi)^2 要求解w和b,观测到的具体值是xi和yi(xi表示第i个观测到的样本)。借助梯度下降的方法,计算出一个极小值,希望一个wx+b逼近于y,即(y-(wx+b))这个的极小值。在这个取到极小值的时候的w’ 和b’ 的值就是我们需要求解的,即获得最能拟合出真实值y情况的w’x+b’。
在这里插入图片描述
loss = (w·xi+b-yi) ^2
∑i (w·xi+b-yi)^2 最小,这个过程是很好理解的:
在这里插入图片描述
loss是大于等于0的,所以b=0,w=1左右,loss是最小的。如果loss函数能够可视化的话,就可以搜索出loss最低点的w和b的值,就可以很直观的求解出loss最小值。但是现实情况往往不能够可视化,因为x的维度非常非常高,使得w的维度也非常高,因此很难在3维图像上将loss函数曲面图绘制出来

凸优化Convex optimization

上图是有一个全局最小点的,这样的函数叫做凸函数,会有专门的学科叫凸优化,对于深度学习,我们只需要了解一下即可,不需要深入了解,只需要会使用现成的凸函数优化器即可,即使是非凸函数,也能找到一个局部最小值。

查看一下求解过程:

随机的一个初始点,w=0,b=0为初始点,然后在每一处对w和b来求导,从而不断跟新w值,最终希望有一条直线穿过整个数据集,使得整体的误差偏小。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

回归问题实战:

在这里插入图片描述
data:

#.csv
32.50234527	31.70700585
53.42680403	68.77759598
61.53035803	62.5623823
47.47563963	71.54663223
59.81320787	87.23092513
55.14218841	78.21151827
52.21179669	79.64197305
39.29956669	59.17148932
48.10504169	75.3312423
52.55001444	71.30087989
45.41973014	55.16567715
54.35163488	82.47884676
44.1640495	62.00892325
58.16847072	75.39287043
56.72720806	81.43619216
48.95588857	60.72360244
44.68719623	82.89250373
60.29732685	97.37989686
45.61864377	48.84715332
38.81681754	56.87721319
66.18981661	83.87856466
65.41605175	118.5912173
47.48120861	57.25181946
41.57564262	51.39174408
51.84518691	75.38065167
59.37082201	74.76556403
57.31000344	95.45505292
63.61556125	95.22936602
46.73761941	79.05240617
50.55676015	83.43207142
52.22399609	63.35879032
35.56783005	41.4128853
42.43647694	76.61734128
58.16454011	96.76956643
57.50444762	74.08413012
45.44053073	66.58814441
61.89622268	77.76848242
33.09383174	50.71958891
36.43600951	62.12457082
37.67565486	60.81024665
44.55560838	52.68298337
43.31828263	58.56982472
50.07314563	82.90598149
43.87061265	61.4247098
62.99748075	115.2441528
32.66904376	45.57058882
40.16689901	54.0840548
53.57507753	87.99445276
33.86421497	52.72549438
64.70713867	93.57611869
38.11982403	80.16627545
44.50253806	65.10171157
40.59953838	65.56230126
41.72067636	65.28088692
51.08863468	73.43464155
55.0780959	71.13972786
41.37772653	79.10282968
62.49469743	86.52053844
49.20388754	84.74269781
41.10268519	59.35885025
41.18201611	61.68403752
50.18638949	69.84760416
52.37844622	86.09829121
50.13548549	59.10883927
33.64470601	69.89968164
39.55790122	44.86249071
56.13038882	85.49806778
57.36205213	95.53668685
60.26921439	70.25193442
35.67809389	52.72173496
31.588117	50.39267014
53.66093226	63.64239878
46.68222865	72.24725107
43.10782022	57.81251298
70.34607562	104.2571016
44.49285588	86.64202032
57.5045333	91.486778
36.93007661	55.23166089
55.80573336	79.55043668
38.95476907	44.84712424
56.9012147	80.20752314
56.86890066	83.14274979
34.3331247	55.72348926
59.04974121	77.63418251
57.78822399	99.05141484
54.28232871	79.12064627
51.0887199	69.58889785
50.28283635	69.51050331
44.21174175	73.68756432
38.00548801	61.36690454
32.94047994	67.17065577
53.69163957	85.66820315
68.76573427	114.8538712
46.2309665	90.12357207
68.31936082	97.91982104
50.03017434	81.53699078
49.23976534	72.11183247
50.03957594	85.23200734
48.14985889	66.22495789
25.12848465	53.45439421
import numpy as np

# y = wx + b
def compute_error_for_line_given_points(b, w, points):
    totalError = 0
    for i in range(0, len(points)):
        x = points[i, 0]
        y = points[i, 1]
        totalError += (y - (w * x + b)) ** 2
    return totalError / float(len(points))

def step_gradient(b_current, w_current, points, learningRate):
    b_gradient = 0
    w_gradient = 0
    N = float(len(points))
    for i in range(0, len(points)):
        x = points[i, 0]
        y = points[i, 1]
        b_gradient += -(2/N) * (y - ((w_current * x) + b_current))
        w_gradient += -(2/N) * x * (y - ((w_current * x) + b_current))
    new_b = b_current - (learningRate * b_gradient)
    new_m = w_current - (learningRate * w_gradient)
    return [new_b, new_m]

def gradient_descent_runner(points, starting_b, starting_m, learning_rate, num_iterations):
    b = starting_b
    m = starting_m
    for i in range(num_iterations):
        b, m = step_gradient(b, m, np.array(points), learning_rate)
    return [b, m]

def run():
    points = np.genfromtxt("data.csv", delimiter=",")
    learning_rate = 0.0001
    initial_b = 0 # initial y-intercept guess
    initial_m = 0 # initial slope guess
    num_iterations = 1000
    print("Starting gradient descent at b = {0}, m = {1}, error = {2}"
          .format(initial_b, initial_m,
                  compute_error_for_line_given_points(initial_b, initial_m, points))
          )
    print("Running...")
    [b, m] = gradient_descent_runner(points, initial_b, initial_m, learning_rate, num_iterations)
    print("After {0} iterations b = {1}, m = {2}, error = {3}".
          format(num_iterations, b, m,
                 compute_error_for_line_given_points(b, m, points))
          )

if __name__ == '__main__':
    run()

在这里插入图片描述
可以发现error由最初的5565.107到112.614810。线性回归的拟合的均方误差明显减小了。

手写数字识别问题案例:

MNIST数据集
这个数据集每一个数字照片大小是28*28,有7000张。将前6000张做训练集,剩余1000张做测试集。
在这里插入图片描述
每一个手写数字图片可以由矩阵来表示,即28行28列,每一个index所对应的值是0到1区间内的值,该值表示灰度值。将这个28行28列的矩阵通过flat操作进行降维,拉伸成1维向量,数据量不变784,这样忽略了位置相关性。在降维后的向量前添加一个维度,使其变为[1,784],这样数据是不变的,只是前面多了一个1。

一个简单的线性模型y=wx+b,但是对于手写数字来说,仅仅使用简单的线性模型是不够的,我们使用三个线性函数的嵌套。
在这里插入图片描述
loss损失函数如何计算:
loss = ([预测结果]-[真实值])^2
这里的真实值,需要转换成独热编码one-hot。
在这里插入图片描述
在这里插入图片描述
对于线性模型很难处理非线性情况,这里引入非线性因子Non-linear Factor。这里非线性因子主要有sigmoid函数(激活函数),ReLU函数等。这里我们选用ReLU函数作为非线性因子。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

将上述过程通过python来实现:

在这里插入图片描述
最后一层不加relu非线性因子:
在这里插入图片描述
主要分为四个步骤对手写数字进行识别:
1、加载图片
2、新建模型
3、训练集
4、测试集
在这里插入图片描述

utils.py:

import  torch
from    matplotlib import pyplot as plt

#绘制下降的曲线:
def plot_curve(data):
    fig = plt.figure()
    plt.plot(range(len(data)), data, color='blue')
    plt.legend(['value'], loc='upper right')
    plt.xlabel('step')
    plt.ylabel('value')
    plt.show()

#绘制图片,识别的结果
def plot_image(img, label, name):
    fig = plt.figure()
    for i in range(6):
        plt.subplot(2, 3, i + 1)
        plt.tight_layout()
        plt.imshow(img[i][0]*0.3081+0.1307, cmap='gray', interpolation='none')
        plt.title("{}: {}".format(name, label[i].item()))
        plt.xticks([])
        plt.yticks([])
    plt.show()

#实现独热编码
def one_hot(label, depth=10):
    out = torch.zeros(label.size(0), depth)
    idx = torch.LongTensor(label).view(-1, 1)
    out.scatter_(dim=1, index=idx, value=1)
    return out

mnist_train.py:

import torch
from torch import nn #表示神经网络的一些工作
from torch.nn import functional as F #表示常用的一些函数
from torch import optim #导入优化工具包

#mnist是一个视觉的数据集,所以需要导入torchvision
import torchvision
from matplotlib import pyplot as plt

#导入utils类,即utils.py文件
from utils import plot_image,plot_curve,one_hot


# step1. load dataset
#这里有一个batch_size概念:
#gpu性能非常强大,一次处理1张图片可能需要3毫秒,一次处理多张图片可能也就4到5毫秒。
#这样通过并行处理多张图片,可以大大节省计算时间。

batch_size = 512 #因为图片是28*28的大小,图片比较小,所以这里batch_size就稍微大一些。

#加载训练集
#torch专门加载数据集的方法:
train_loader = torch.utils.data.DataLoader(
    #加载mnist数据集
    #参数1:指定数据集
    #参数2train:指定下载的数据是那60000张训练集数据集,因为有60000张图片是做训练集的,有10000张是测试集的。
    #参数3download:表示如果当前文件没有mnist数据集,就从网络上下载下来。
    #参数4transform:转换格式,网络下载下来的数据集是numpy格式,需要转换成Tensor格式,该格式是torch的一个数据载体。
    #此外还有一个正则化过程normalize,因为神经网络所接受的数据最好均匀分布在0附近,但是我们图片的像素是0到1之间(0的右侧分布),通过减去0.1307再除以标准差这样一个过程使得我们的数据能够在0周围均匀的分布,这样更方便神经网络去优化,这个正则化可以不做,注释掉的话性能会差一些可能70%作用,如果使用正则化效果会好很多80%多。
    torchvision.datasets.MNIST('mnist_data', train=True, download=True,
                               transform=torchvision.transforms.Compose([
                                   torchvision.transforms.ToTensor(),
                                   torchvision.transforms.Normalize(
                                       (0.1307,), (0.3081,))
                               ])),
    # 参数5batch_size:一次加载多少张图片,并行处理多张图片。
    # 参数6shuffle:表示加载的时候随机打散
    batch_size=batch_size, shuffle=True)

#加载测试集
test_loader = torch.utils.data.DataLoader(
    #这里参数shuffle设置为false,因为测试集就没必要打散了
    torchvision.datasets.MNIST('mnist_data/', train=False, download=True,
                               transform=torchvision.transforms.Compose([
                                   torchvision.transforms.ToTensor(),
                                   torchvision.transforms.Normalize(
                                       (0.1307,), (0.3081,))
                               ])),
    batch_size=batch_size, shuffle=False)

#将加载的图片显示出来
#next() 返回迭代器的下一个项目。
#next() 函数要和生成迭代器的 iter() 函数一起使用。
#iter() 函数用来生成迭代器,iter(object[, sentinel]),object -- 支持迭代的集合对象。
#sentinel -- 如果传递了第二个参数,则参数 object 必须是一个可调用的对象(如,函数),此时,iter 创建了一个迭代器对象,每次调用这个迭代器对象的__next__()方法时,都会调用 object。
x,y = next(iter(train_loader))
#512张图片,1个通道,28行,28列,label有512个,最小值-0.4242,最大值2.8215,说明在0周围分布。
print(train_loader)
print(x.shape,y.shape,x.min(),x.max())
# print(x)
plot_image(x,y,'image sample')


#step2.create net
class Net(nn.Module):

    def __init__(self):
        super(Net,self).__init__()

        #xw+b
        self.fc1 = nn.Linear(28*28,256)
        self.fc2 = nn.Linear(256,64)
        self.fc3 = nn.Linear(64,10)

    def forward(self,x):
        # x:[b,1,28,28]
        # h1 = xw+b
        x = F.relu(self.fc1(x))
        # h2 = relu(h1w2+b2)
        x = F.relu(self.fc2(x))
        # h3 = h2w3+b3
        x=self.fc3(x)
        return x

net = Net()
#[w1,b1,w2,b2,w3,b3]
optimizer = optim.SGD(net.parameters(),lr=0.01,momentum=0.9)


#step.3 trainning

train_loss = []
for epoch in range(3):
    for batch_idx,(x,y) in enumerate(train_loader):
        # x: [b,1,28,28],y:[512]
        # [b,1,28,28] => [b , feature]
        x= x.view(x.size(0),28*28)
        # => [b,10]
        out = net(x)
        #[b,10]
        y_onehot = one_hot(y)
        #loss = mse(out , y_onehot)
        loss = F.mse_loss(out,y_onehot)

        optimizer.zero_grad()
        loss.backward()
        #w' = w - lr*grad
        optimizer.step()

        train_loss.append(loss.item())

        if batch_idx % 10 ==0:
            print(epoch,batch_idx,loss.item())

plot_curve(train_loss)
#we get optimal [w1,b1,w2,b2,w3,b3]

#step.4 accuracy

total_correct = 0
for x,y in test_loader:
    x = x.view(x.size(0),28*28)
    out = net(x)
    #out :[b,10] => pred:[b]
    pred = out.argmax(dim = 1)
    correct = pred.eq(y).sum().float().item()
    total_correct += correct

total_num = len(test_loader.dataset)
acc = total_correct/total_num
print('test acc:',acc)

#step.5
x,y = next(iter(test_loader))
out = net(x.view(x.size(0),28*28))
pred = out.argmax(dim =1)
plot_image(x,pred,'test')

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Henrik698

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值