Day1:线性回归、softmax与分类模型、多层感知机 学习打卡

想要一起学习的小伙伴可以在伯禹学习平台和K-Lab合作推出的《动手学深度学习》代码讲解里共同学习,附上链接:
禹伯学习平台

一、线性回归

首先是线性回归理论基础,基础薄弱些就复习下:
1.线性回归的基本要素
1)模型:
为了简单起见,这里我们假设价格只取决于房屋状况的两个因素,即面积(平方米)和房龄(年)。接下来我们希望探索价格与这两个因素的具体关系。线性回归假设输出与各个输入之间是线性关系:
price=warea⋅area+wage⋅age+b
2)数据集
我们通常收集一系列的真实数据,例如多栋房屋的真实售出价格和它们对应的面积和房龄。我们希望在这个数据上面寻找模型参数来使模型的预测价格与真实价格的误差最小。在机器学习术语里,该数据集被称为训练数据集(training data set)或训练集(training set),一栋房屋被称为一个样本(sample),其真实售出价格叫作标签(label),用来预测标签的两个因素叫作特征(feature)。特征用来表征样本的特点。
3)损失函数
在模型训练中,我们需要衡量价格预测值与真实值之间的误差。一个常用的选择是平方函数。 它在评估索引为 i 的样本误差的表达式为
l(i)(w,b)=12(y^(i)−y(i))2,

【小l指的是单个的平方函数误差】

L(w,b)=1n∑i=1nl(i)(w,b)=1n∑i=1n12(w⊤x(i)+b−y(i))2.

【大L指的是小批量的样本误差】

4)优化函数 - 随机梯度下降
在求数值解的优化算法中,小批量随机梯度下降(mini-batch stochastic gradient descent)在深度学习中被广泛使用。它的算法很简单:先选取一组模型参数的初始值,如随机选取;接下来对参数进行多次迭代,使每次迭代都可能降低损失函数的值。在每次迭代中,先随机均匀采样一个由固定数目训练数据样本所组成的小批量(mini-batch) B ,然后求小批量中数据样本的平均损失有关模型参数的导数(梯度),最后用此结果与预先设定的一个正数的乘积作为模型参数在本次迭代的减小量。

【小结: 优化数值解:小批量随机梯度下降 步骤: 初始值(经过随机初始化)——>多次迭代(每次降低损失函数的值)——>采样一个小批量数据,求它平均损失有关模型的导数——>导数乘上一个正数得出减少量
也就是: (w,b)←(w,b)−η|B|∑i∈B∂(w,b)l(i)(w,b)】

学习率: η 代表在每次优化中,能够学习的步长的大小
批量大小: B 是小批量计算中的批量大小batch size

总结一下,优化函数的有以下两个步骤:
(i)初始化模型参数,一般来说使用随机初始化;
(ii)我们在数据上迭代多次,通过在负梯度方向移动参数来更新每个参数。

下面来看代码:

线性回归模型从零开始的实现

# import packages and modules
%matplotlib inline
import torch
from IPython import display
from matplotlib import pyplot as plt
import numpy as np
import random

print(torch.__version__)

生成数据集
使用线性模型来生成数据集,生成一个1000个样本的数据集,下面是用来生成数据的线性关系:

price=warea⋅area+wage⋅age+b
 
# set input feature number 
num_inputs = 2
# set example number
num_examples = 1000

# set true weight and bias in order to generate corresponded label

true_w = [2, -3.4]
true_b = 4.2

features = torch.randn(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(0, 0.01, size=labels.size()),
                       dtype=torch.float32)

使用图像来展示生成的数据

plt.scatter(features[:, 1].numpy(), labels.numpy(), 1);

读取数据集

def data_iter(batch_size, features, labels):
    num_examples = len(features)
    indices = list(range(num_examples))
    random.shuffle(indices)  # random read 10 samples
    for i in range(0, num_examples, batch_size):
        j = torch.LongTensor(indices[i: min(i + batch_size, num_examples)]) # the last time may be not enough for a whole batch
        yield  features.index_select(0, j), labels.index_select(0, j)
batch_size = 10

for X, y in data_iter(batch_size, features, labels):
    print(X, '\n', y)
    break

初始化模型参数

w = torch.tensor(np.random.normal(0, 0.01, (num_inputs, 1)), dtype=torch.float32)
b = torch.zeros(1, dtype=torch.float32)

w.requires_grad_(requires_grad=True)
b.requires_grad_(requires_grad=True)

在此处附加梯度之后,才能在后面反向传播求出梯度

定义模型
定义用来训练参数的训练模型:

price=warea⋅area+wage⋅age+b
 
def linreg(X, w, b):
    return torch.mm(X, w) + b

定义损失函数
我们使用的是均方误差损失函数:

l(i)(w,b)=12(y^(i)−y(i))2,
 
def squared_loss(y_hat, y): 
    return (y_hat - y.view(y_hat.size())) ** 2 / 2

定义优化函数
在这里优化函数使用的是小批量随机梯度下降:

(w,b)←(w,b)−η|B|∑i∈B∂(w,b)l(i)(w,b)

 
def sgd(params, lr, batch_size): 
    for param in params:
        param.data -= lr * param.grad / batch_size # ues .data to operate param without gradient track

训练
当数据集、模型、损失函数和优化函数定义完了之后就可来准备进行模型的训练了。

# super parameters init
lr = 0.03
num_epochs = 5

net = linreg
loss = squared_loss

# training
for epoch in range(num_epochs):  # training repeats num_epochs times
    # in each epoch, all the samples in dataset will be used once
    
    # X is the feature and y is the label of a batch sample
    for X, y in data_iter(batch_size, features, labels):
        l = loss(net(X, w, b), y).sum()  
        # calculate the gradient of batch sample loss 
        l.backward()  
        # using small batch random gradient descent to iter model parameters
        sgd([w, b], lr, batch_size)  
        # reset parameter gradient
        w.grad.data.zero_()
        b.grad.data.zero_()
    train_l = loss(net(features, w, b), labels)
    print('epoch %d, loss %f' % (epoch + 1, train_l.mean().item()))
w, true_w, b, true_b

softmax与分类模型

1.softmax的基本概念
1)分类问题
一个简单的图像分类问题,输入图像的高和宽均为2像素,色彩为灰度。
图像中的4像素分别记为 x1,x2,x3,x4 。
假设真实标签为狗、猫或者鸡,这些标签对应的离散值为 y1,y2,y3 。
我们通常使用离散的数值来表示类别,例如 y1=1,y2=2,y3=3 。

2)权重矢量
o1=x1w11+x2w21+x3w31+x4w41+b1

o2=x1w12+x2w22+x3w32+x4w42+b2

o3=x1w13+x2w23+x3w33+x4w43+b3

关于全连接层和全连接层权重的问题
https://blog.csdn.net/qq_39521554/article/details/81385159
https://blog.csdn.net/IT_BOY__/article/details/100552298

3)神经网络图
下图用神经网络图描绘了上面的计算。softmax回归同线性回归一样,也是一个单层神经网络。由于每个输出 o1,o2,o3 的计算都要依赖于所有的输入 x1,x2,x3,x4 ,softmax回归的输出层也是一个全连接层。
Image Name
softmax回归是一个单层神经网络

既然分类问题需要得到离散的预测输出,一个简单的办法是将输出值 oi 当作预测类别是 i 的置信度,并将值最大的输出所对应的类作为预测输出,即输出 argmaxioi 。例如,如果 o1,o2,o3 分别为 0.1,10,0.1 ,由于 o2 最大,那么预测类别为2,其代表猫。

直接使用输出层的输出有两个问题:
一方面,由于输出层的输出值的范围不确定,我们难以直观上判断这些值的意义。例如,刚才举的例子中的输出值10表示“很置信”图像类别为猫,因为该输出值是其他两类的输出值的100倍。但如果 o1=o3=103 ,那么输出值10却又表示图像类别为猫的概率很低。
另一方面,由于真实标签是离散值,这些离散值与不确定范围的输出值之间的误差难以衡量。
softmax运算符(softmax operator)解决了以上两个问题。它通过下式将输出值变换成值为正且和为1的概率分布:

y1,y2,y^3=softmax(o1,o2,o3)

其中

y1=exp(o1)∑3i=1exp(oi),y2=exp(o2)∑3i=1exp(oi),y^3=exp(o3)∑3i=1exp(oi).

容易看出 y1+y2+y^3=1 且 0≤y1,y2,y^3≤1 ,因此 y1,y2,y^3 是一个合法的概率分布。这时候,如果 y^2=0.8 ,不管 y^1 和 y^3 的值是多少,我们都知道图像类别为猫的概率是80%。

因此softmax运算不改变预测类别输出。

小批量矢量计算表达式
为了进一步提升计算效率,我们通常对小批量数据做矢量计算。广义上讲,给定一个小批量样本,其批量大小为 n ,输入个数(特征数)为 d ,输出个数(类别数)为 q 。设批量特征为 X∈Rn×d 。假设softmax回归的权重和偏差参数分别为 W∈Rd×q 和 b∈R1×q 。softmax回归的矢量计算表达式为
OY^=XW+b,=softmax(O),

其中的加法运算使用了广播机制, O,Y^∈Rn×q 且这两个矩阵的第 i 行分别为样本 i 的输出 o(i) 和概率分布 y^(i) 。

交叉熵损失函数
对于样本 i ,我们构造向量 y(i)∈Rq ,使其第 y(i) (样本 i 类别的离散数值)个元素为1,其余为0。这样我们的训练目标可以设为使预测概率分布 y^(i) 尽可能接近真实的标签概率分布 y(i) 。

平方损失估计
Loss=|y^(i)−y(i)|2/2

刚刚线性回归运用的损失函数

然而,想要预测分类结果正确,我们其实并不需要预测概率完全等于标签概率。例如,在图像分类的例子里,如果 y(i)=3 ,那么我们只需要 y^(i)3 比其他两个预测值 y^(i)1 和 y^(i)2 大就行了。即使 y^(i)3 值为0.6,不管其他两个预测值为多少,类别预测均正确。而平方损失则过于严格,例如 y(i)1=y(i)2=0.2 比 y(i)1=0,y(i)2=0.4 的损失要小很多,虽然两者都有同样正确的分类预测结果。

改善上述问题的一个方法是使用更适合衡量两个概率分布差异的测量函数。其中,交叉熵(cross entropy)是一个常用的衡量方法:
H(y(i),y(i))=−∑j=1qy(i)jlogy(i)j,

交叉熵的结果足够大就可以保证结果正确

模型训练和预测
我们将使用准确率(accuracy)来评价模型的表现。它等于正确预测数量与总预测数量之比。

下面看代码:
这里会使用torchvision包,它是服务于PyTorch深度学习框架的,主要用来构建计算机视觉模型。torchvision主要由以下几部分构成:

torchvision.datasets: 一些加载数据的函数及常用的数据集接口;
torchvision.models: 包含常用的模型结构(含预训练模型),例如AlexNet、VGG、ResNet等;
torchvision.transforms: 常用的图片变换,例如裁剪、旋转等;
torchvision.utils: 其他的一些有用的方法。

二、softmax从零开始的实现

import torch
import torchvision
import numpy as np
import sys
sys.path.append("/home/kesci/input")
import d2lzh1981 as d2l

print(torch.__version__)
print(torchvision.__version__)

1.3.0
0.4.1a0+d94043a
获取训练集数据和测试集数据

batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

模型参数初始化

num_inputs = 784
print(28*28)
num_outputs = 10

W = torch.tensor(np.random.normal(0, 0.01, (num_inputs, num_outputs)), dtype=torch.float)
b = torch.zeros(num_outputs, dtype=torch.float)
784
W.requires_grad_(requires_grad=True)
b.requires_grad_(requires_grad=True)
tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], requires_grad=True)

对多维Tensor按维度操作

X = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(X.sum(dim=0, keepdim=True))  # dim为0,按照相同的列求和,并在结果中保留列特征
print(X.sum(dim=1, keepdim=True))  # dim为1,按照相同的行求和,并在结果中保留行特征
print(X.sum(dim=0, keepdim=False)) # dim为0,按照相同的列求和,不在结果中保留列特征
print(X.sum(dim=1, keepdim=False)) # dim为1,按照相同的行求和,不在结果中保留行特征
tensor([[5, 7, 9]])
tensor([[ 6],
        [15]])
tensor([5, 7, 9])
tensor([ 6, 15])

定义softmax操作

y^j=exp(oj)∑3i=1exp(oi)
 
def softmax(X):
    X_exp = X.exp()
    partition = X_exp.sum(dim=1, keepdim=True)
    # print("X size is ", X_exp.size())
    # print("partition size is ", partition, partition.size())
    return X_exp / partition  # 这里应用了广播机制
X = torch.rand((2, 5))
X_prob = softmax(X)
print(X_prob, '\n', X_prob.sum(dim=1))
tensor([[0.1927, 0.2009, 0.1823, 0.1887, 0.2355],
        [0.1274, 0.1843, 0.2536, 0.2251, 0.2096]]) 
 tensor([1., 1.])

softmax回归模型

o(i)y^(i)=x(i)W+b,=softmax(o(i)).
 
def net(X):
    return softmax(torch.mm(X.view((-1, num_inputs)), W) + b)

定义损失函数


H(y(i),y^(i))=−∑j=1qy(i)jlogy^(i)j,
 
ℓ(Θ)=1n∑i=1nH(y(i),y^(i)),
 
ℓ(Θ)=−(1/n)∑i=1nlogy^(i)y(i)
 
y_hat = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
y = torch.LongTensor([0, 2])
y_hat.gather(1, y.view(-1, 1))
tensor([[0.1000],
        [0.5000]])
def cross_entropy(y_hat, y):
    return - torch.log(y_hat.gather(1, y.view(-1, 1)))

定义准确率
我们模型训练完了进行模型预测的时候,会用到我们这里定义的准确率。

def accuracy(y_hat, y):
    return (y_hat.argmax(dim=1) == y).float().mean().item()
print(accuracy(y_hat, y))

0.5


#本函数已保存在d2lzh_pytorch包中方便以后使用。该函数将被逐步改进:它的完整实现将在“图像增广”一节中描述

def evaluate_accuracy(data_iter, net):
    acc_sum, n = 0.0, 0`在这里插入代码片`
    for X, y in data_iter:
        acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
        n += y.shape[0]
    return acc_sum / n
print(evaluate_accuracy(test_iter, net))

0.1457
训练模型

num_epochs, lr = 5, 0.1

#本函数已保存在d2lzh_pytorch包中方便以后使用

def train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size,
              params=None, lr=None, optimizer=None):
    for epoch in range(num_epochs):
        train_l_sum, train_acc_sum, n = 0.0, 0.0, 0
        for X, y in train_iter:
            y_hat = net(X)
            l = loss(y_hat, y).sum()
            
            # 梯度清零
            if optimizer is not None:
                optimizer.zero_grad()
            elif params is not None and params[0].grad is not None:
                for param in params:
                    param.grad.data.zero_()
            
            l.backward()
            if optimizer is None:
                d2l.sgd(params, lr, batch_size)
            else:
                optimizer.step() 
            
            
            train_l_sum += l.item()
            train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item()
            n += y.shape[0]
        test_acc = evaluate_accuracy(test_iter, net)
        print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f'
              % (epoch + 1, train_l_sum / n, train_acc_sum / n, test_acc))

train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, batch_size, [W, b], lr)

epoch 1, loss 0.7870, train acc 0.751, test acc 0.794
epoch 2, loss 0.5702, train acc 0.813, test acc 0.809
epoch 3, loss 0.5254, train acc 0.826, test acc 0.814
epoch 4, loss 0.5009, train acc 0.832, test acc 0.822
epoch 5, loss 0.4853, train acc 0.837, test acc 0.828

模型预测
现在我们的模型训练完了,可以进行一下预测,我们的这个模型训练的到底准确不准确。 现在就可以演示如何对图像进行分类了。给定一系列图像(第三行图像输出),我们比较一下它们的真实标签(第一行文本输出)和模型预测结果(第二行文本输出)。

X, y = iter(test_iter).next()

true_labels = d2l.get_fashion_mnist_labels(y.numpy())
pred_labels = d2l.get_fashion_mnist_labels(net(X).argmax(dim=1).numpy())
titles = [true + '\n' + pred for true, pred in zip(true_labels, pred_labels)]

d2l.show_fashion_mnist(X[0:9], titles[0:9])

简洁实现时,定义网络模型时要定义一个额外的转换层

三、 多层感知机

1.多层感知机的基本知识
深度学习主要关注多层模型。在这里,我们将以多层感知机(multilayer perceptron,MLP)为例,介绍多层神经网络的概念。

隐藏层
下图展示了一个多层感知机的神经网络图,它含有一个隐藏层,该层中有5个隐藏单元。

Image Name

表达公式:
O=(XWh+bh)Wo+bo=XWhWo+bhWo+bo.
从式子可以看出,虽然神经网络引入了隐藏层,却依然等价于一个单层神经网络:其中输出层权重参数为 WhWo ,偏差参数为 bhWo+bo 。不难发现,即便再添加更多的隐藏层,以上设计依然只能与仅含输出层的单层神经网络等价。

关于激活函数的选择:
ReLu函数是一个通用的激活函数,目前在大多数情况下使用。但是,ReLU函数只能在隐藏层中使用。

用于分类器时,sigmoid函数及其组合通常效果更好。由于梯度消失问题,有时要避免使用sigmoid和tanh函数。

在神经网络层数较多的时候,最好使用ReLu函数,ReLu函数比较简单计算量少,而sigmoid和tanh函数计算量大很多。

在选择激活函数的时候可以先选用ReLu函数如果效果不理想可以尝试其他激活函数。
下面来看代码:从零实现多层感知机

简洁实现多层感知机时,和从零实现大同小异,只是在定义模型时多了隐藏层的参数,需要定义激活函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值