一、深度学习和PyTorch基础

视频学习

人工智能:

1956年“达特茅斯”会议上人工智能概念诞生

使一部机器像人一样进行感知、认知、决策、执行的人工程序或系统

机器学习:

定义:从数据中自动提取知识

用途:

  1. 计算机视觉:人脸识别、图像分类、目标检测、图像搜索、图像分割和视频监控
  2. 语音:语音唤醒、语音识别、语音合成和声纹识别
  3. 自然语言处理:文本分类、机器翻译、知识图谱、自动回答、信息检索和文本生成

三要素:

  1. 模型:对要学习问题映射的假设(问题建模,确定假设空间)
    • 数据标记
      • 监督学习:样本具有标记
      • 无监督学习:样本没有标记
      • 半监督学习:部分样本有标记
      • 强化学习:样本标记未知,但知道与输出目标相关的反馈
    • 数据分布
      • 参数模型:对数据分布进行假设,待求解的数据模式可以用一组有限且固定的参数进行刻画,比如:线性回归、逻辑回归、感知机、K均值聚类
      • 非参数模型:不对数据分布进行假设,数据的所有统计特性来源于数据本身
    • 建模对象
      • 判别模型:先从数据中学习联合概率分布P(x,y),然后利用朴素贝叶斯公式求出P(Y|X),比如:朴素贝叶斯、隐马尔可夫、马尔科夫随机场
      • 生成模型:输入特征x,直接预测出最可能的Y,比如:SVM、逻辑回归、条件随机场、决策树
  2. 策略:从假设空间中学习/选择最优模型的准则(确定目标函数)
  3. 算法:根据目标函数求解最优模型的具体计算方法(求解模型参数)

机器学习主要解决对一个问题建模去确定假设空间,在假设空间里基于一个训练数据建立一个目标函数

深度学习

扫描二维码关注公众号,回复: 11439347 查看本文章

发展历程:

  1. 感知器出现,认为感知器无所不能,但是实际上无法解决异或门问题;
  2. BP算法:Rumelhart和Hinton合作在Nature杂志上发表论文,第一次简洁地阐述了
  3. BP算法,在神经网络里增加一个所谓的隐层,解决了XOR难题;
  4. CNN网络:Yann Lecun在1989年发表了论文,之后又进一步运用了一种叫做卷积神经网络的技术,最开始是运用于银行对数字的识别;
  5. Vladmir Vapnik提出了SVM,把神经网络推向寒冬;
  6. Hinton拿到资金后,将神经网络更名为深度学习;
  7. 吴恩达2009年发表了论文,解决了速度问题,使用GPU运行速度和用传统双核CPU相比,最快时要快近70倍;
  8. 李菲菲建立第一个超大型图像数据库供计算机视觉研究者使用;
  9. Hinton和两个研究生利用CNN+Dropout+Relu激励函数将ILSVRC的错误率降到了15.3%,是人工智能技术突破的一个转折点;
  10. Yoshua Bengio在2011年发表论文,提出了一种修正的relu激励函数,解决了传统激励函数在反向传播计算中的梯度消失问题;
  11. Schmidhuber和他的学生提出来长短期记忆的计算模型。

神经网络的三起三落

深度学习使用两个优化器:Adam和SGD,一般来说,使用Adam就可以,效果会比较好

深度学习的6个不能:

  1. 算法输出不稳定,容易被攻击
  2. 模型复杂度高,难以纠错和调试(无论给什么无意义的 都倾向给一个有意义的输出)
  3. 模型层次符合度高,参数不透明
  4. 端到端训练方式对数据依赖性强,模型增量性差
  5. 专注直观感知类问题,对开放性推理问题无能为力
  6. 人类指示无法有效引入进行监督,机器偏见难以避免

M-P神经元:

在神经元总数相等的情况下,增加网络深度可以比增加宽度带来更强的网络表示能力:产生更多的线性区域

激活函数:

什么情况下使用sigmoid函数:无论输入什么永远在0-1之间,可以用于二分类,可以用预算值控制准确率,多分类用softmart

梯度:

梯度下降:

梯度消失:

对于激活函数,之前一直使用Sigmoid函数,其函数图像成一个S型,它会将正无穷到负无穷的数映射到01之间。当我们对Sigmoid函数求导时,会呈现一个驼峰状(很像高斯函数),从求导结果可以看出,Sigmoid导数的取值范围在00.25之间,而我们初始化的网络权值通常都小于1,因此,当层数增多时,小于0的值不断相乘,最后就导致梯度消失的情况出现。

梯度消失解决办法:

  1. 用无监督数据作分层与训练,再用有监督数据fine-tune
  2. 使用Relu激活函数
  3. 使用辅助损失函数
  4. 逐层的尺度归一

自编码器:

假设输出与输入相同,是一种尽可能复现输入信号的神经网络,没有额外的监督信息,其中间隐层是代表输入的特征,可以最大程度上代表原输入信号,可以用来图像去噪

受限玻尔兹曼机:

RBM是两层神经网络,包括可见层和隐藏层,隐藏层可以作为可见层输入的特征

图像处理基本练习

1.下载并显示图像

!wget https://raw.githubusercontent.com/summitgao/ImageGallery/master/yeast_colony_array.jpg
import matplotlib
import numpy as np
import matplotlib.pyplot as plt

import skimage
from skimage import data
from skimage import io

colony = io.imread('yeast_colony_array.jpg')
print(type(colony))
print(colony.shape)
<class 'numpy.ndarray'>
(406, 604, 3)
# 绘制真实图像的所有通道
plt.subplot(121)
plt.imshow(colony[:,:,:])
plt.title('3-channel image')
plt.axis('off')

# 仅绘制一个通道
plt.subplot(122)
plt.imshow(colony[:,:,0])
plt.title('1-channel image')
plt.axis('off');

2.读取并改变图像像素值

# 获取第10行第20列的像素值
camera = data.camera()
print(camera[10, 20])

# 把这个地方设为黑色
camera[30:100, 10:100] = 0
plt.imshow(camera, 'gray')
153
<matplotlib.image.AxesImage at 0x7fe7d71f0048>

# 把第一行设为黑色
camera = data.camera()
camera[:10] = 0
plt.imshow(camera, 'gray')
<matplotlib.image.AxesImage at 0x7fe7d715f358>

# 把mask为true的地方设为白色像素
camera = data.camera()
mask = camera < 80
camera[mask] = 255
plt.imshow(camera, 'gray')
<matplotlib.image.AxesImage at 0x7fe7d714ba90>

# 改变真实图像的颜色
cat = data.chelsea()
plt.imshow(cat)
<matplotlib.image.AxesImage at 0x7fe7d71944e0>

# 把较亮的像素设为红色
red_cat = cat.copy()
reddish = cat[:, :, 0] > 160
red_cat[reddish] = [255, 0, 0]
plt.imshow(red_cat)
<matplotlib.image.AxesImage at 0x7fd4fc7cd320>

# 将openCV的RGB颜色改为BGR
BGR_cat = cat[:, :, ::-1]
plt.imshow(BGR_cat)
<matplotlib.image.AxesImage at 0x7fe7d70c5160>

3.转换图像数据类型

from skimage import img_as_float, img_as_ubyte
float_cat = img_as_float(cat)
uint_cat = img_as_ubyte(float_cat)
  • img_as_float Convert to 64-bit floating point.
  • img_as_ubyte Convert to 8-bit uint.
  • img_as_uint Convert to 16-bit uint.
  • img_as_int Convert to 16-bit int

4.显示图像直方图

img = data.camera()
plt.hist(img.ravel(), bins=256, histtype='step', color='black');

5.图像分割

# 用colony image进行图像分割
colony = io.imread('yeast_colony_array.jpg')

# 绘制直方图
img = skimage.color.rgb2gray(colony)
plt.hist(img.ravel(), bins=256, histtype='step', color='black');

# 阙值化
plt.imshow(img>0.5)
<matplotlib.image.AxesImage at 0x7fe7d6f48c50>

6.使用Canny算子进行边缘检测

from skimage.feature import canny
from scipy import ndimage as ndi
img_edges = canny(img)
img_filled = ndi.binary_fill_holes(img_edges)

# Plot
plt.figure(figsize=(18, 12))
plt.subplot(121)
plt.imshow(img_edges, 'gray')
plt.subplot(122)
plt.imshow(img_filled, 'gray')
<matplotlib.image.AxesImage at 0x7fe7c8cdc9b0>

7.改变图像的对比度

# 例图
img = data.camera()
plt.imshow(img, 'gray')
<matplotlib.image.AxesImage at 0x7fe7c8bb3080>

from skimage import exposure
# 拉伸对比
p2, p98 = np.percentile(img, (2, 98))
img_rescale = exposure.rescale_intensity(img, in_range=(p2, p98))
plt.imshow(img_rescale, 'gray')
<matplotlib.image.AxesImage at 0x7fe7c8a9c400>

# 均衡化
img_eq = exposure.equalize_hist(img)
plt.imshow(img_eq, 'gray')
<matplotlib.image.AxesImage at 0x7fe7c8a717f0>

# 自适应均衡
img_adapteq = exposure.equalize_adapthist(img, clip_limit=0.03)
plt.imshow(img_adapteq, 'gray')
<matplotlib.image.AxesImage at 0x7fe7c89d0978>

# 结果展示
def plot_img_and_hist(img, axes, bins=256):
    """Plot an image along with its histogram and cumulative histogram.

    """
    img = img_as_float(img)
    ax_img, ax_hist = axes
    ax_cdf = ax_hist.twinx()

    # 图片展示
    ax_img.imshow(img, cmap=plt.cm.gray)
    ax_img.set_axis_off()
    ax_img.set_adjustable('box')

    # 直方图展示
    ax_hist.hist(img.ravel(), bins=bins, histtype='step', color='black')
    ax_hist.ticklabel_format(axis='y', style='scientific', scilimits=(0, 0))
    ax_hist.set_xlabel('Pixel intensity')
    ax_hist.set_xlim(0, 1)
    ax_hist.set_yticks([])

    # 分布函数展示
    img_cdf, bins = exposure.cumulative_distribution(img, bins)
    ax_cdf.plot(bins, img_cdf, 'r')
    ax_cdf.set_yticks([])

    return ax_img, ax_hist, ax_cdf
fig = plt.figure(figsize=(16, 8))
axes = np.zeros((2, 4), dtype=np.object)
axes[0, 0] = fig.add_subplot(2, 4, 1)
for i in range(1, 4):
    axes[0, i] = fig.add_subplot(2, 4, 1+i, sharex=axes[0,0], sharey=axes[0,0])
for i in range(0, 4):
    axes[1, i] = fig.add_subplot(2, 4, 5+i)

ax_img, ax_hist, ax_cdf = plot_img_and_hist(img, axes[:, 0])
ax_img.set_title('Low contrast image')

y_min, y_max = ax_hist.get_ylim()
ax_hist.set_ylabel('Number of pixels')
ax_hist.set_yticks(np.linspace(0, y_max, 5))

ax_img, ax_hist, ax_cdf = plot_img_and_hist(img_rescale, axes[:, 1])
ax_img.set_title('Contrast stretching')

ax_img, ax_hist, ax_cdf = plot_img_and_hist(img_eq, axes[:, 2])
ax_img.set_title('Histogram equalization')

ax_img, ax_hist, ax_cdf = plot_img_and_hist(img_adapteq, axes[:, 3])
ax_img.set_title('Adaptive equalization')

ax_cdf.set_ylabel('Fraction of total intensity')
ax_cdf.set_yticks(np.linspace(0, 1, 5))

fig.tight_layout()
plt.show()

Pytorch基础训练

什么是Pytorch?

Pytorch是一Python的一个库,主要提供了两个高级功能:

  1. GPU加速的张量运算
  2. 构建在反向自动求导系统上的深度神经网络

Pytorch基础代码练习

1.定义数据

张量类 torch.Tensor 可以代表任意类型的数据

import torch

# 创建一个数
x = torch.tensor(7777777)
print(x)
tensor(7777777)
# 创建一维数组
x = torch.tensor([1,2,3,4,5,6,7])
print(x)
tensor([1, 2, 3, 4, 5, 6,7])
# 创建二维数组
x = torch.ones(2,3)
print(x)
tensor([[1., 1., 1.],
        [1., 1., 1.]])
# 创建任意维度数组
x = torch.ones(3,3,3)
print(x)
tensor([[[1., 1., 1.],
         [1., 1., 1.],
         [1., 1., 1.]],

        [[1., 1., 1.],
         [1., 1., 1.],
         [1., 1., 1.]],

        [[1., 1., 1.],
         [1., 1., 1.],
         [1., 1., 1.]]])
# 创建空张量
x = torch.empty(3,3)
print(x)
tensor([[4.1825e-38, 0.0000e+00, 1.5975e-43],
        [1.3873e-43, 1.4574e-43, 6.4460e-44],
        [1.4153e-43, 1.5274e-43, 1.5695e-43]]
# 创建一个随机初始化的张量
x = torch.rand(3,3)
print(x)
tensor([[0.7217, 0.6777, 0.7724],
        [0.9061, 0.5884, 0.7075],
        [0.5336, 0.5464, 0.5254]])
# 创建一个全0的张量,里面的数据类型为long
x = torch.zeros(3,3,dtype=torch.long)
print(x)
tensor([[0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]])
# 基于现有的tensor,创建一个新tensor,使其继承原有tensor的属性
y = x.new_ones(3,3) 
print(y)
tensor([[1, 1, 1],
        [1, 1, 1],
        [1, 1, 1]])
# 利用原来的tensor的大小,但是重新定义了数据类型
z = torch.randn_like(x, dtype=torch.float)  
print(z)
tensor([[-2.2243, -1.1945,  0.2978],
        [-0.6159,  0.6593,  1.6294],
        [ 0.2331,  0.3410, -0.1972]])

2.定义操作

凡是用Tensor来进行各种运算的,都是Function

基本运算包: abs/sqrt/div/exp/fmod/pow ,一些三角函数 cos/ sin/ asin/ atan2/ cosh,及 ceil/round/floor/trunc ......

布尔运算: gt/lt/ge/le/eq/ne,topk, sort, max/min

线性计算: trace, diag, mm/bmm,t,dot/cross,inverse,svd ......

# 创建一个2x3的tensor
m = torch.tensor([[2,5,3],[4,2,1]])
print(m.size(0),m.size(1),m.size(),sep = '--')
2--3--torch.Size([2, 3])
# 返回 m 中元素的数量
print(m.numel())
6
# 返回 第1行,第1列的数
print(m[1][1])
tensor(2)
# 返回 第1列的全部元素 :代表所有的
print(m[:, 1])
tensor([5, 2]
# 创建数字1-4
# 注意这里结果是1到3,没有4
v = torch.arange(1, 4)
print(v)
tensor([1, 2, 3])
# 点乘
m @ v
m[[0], :] @ v
tensor([21, 11])
tensor([21])
# 加法
m + torch.rand(2, 3)
tensor([[2.8847, 5.8693, 3.5941],
        [4.3565, 2.5205, 1.0334]])
# 转置,由2x3变为3x2
print(m.t())
print(m.transpose(0, 1))
tensor([[2, 4],
        [5, 2],
        [3, 1]])
tensor([[2, 4],
        [5, 2],
        [3, 1]])
# 返回3到7之间等距的20个数
torch.linspace(3,7,20)
tensor([3.0000, 3.2105, 3.4211, 3.6316, 3.8421, 4.0526, 4.2632, 4.4737, 4.6842,
        4.8947, 5.1053, 5.3158, 5.5263, 5.7368, 5.9474, 6.1579, 6.3684, 6.5789,
        6.7895, 7.0000])
from matplotlib import pyplot as plt

# matlabplotlib 只能显示numpy类型的数据,下面展示了转换数据类型,然后显示
# 注意 randn 是生成均值为 0, 方差为 1 的随机数
# 下面是生成 1000 个随机数,并按照 100 个 bin 统计直方图
plt.hist(torch.randn(1000).numpy(), 100);

# 当数据非常非常多的时候,正态分布会体现的非常明显
plt.hist(torch.randn(10**6).numpy(), 100);

# 创建两个 1x4 的tensor
a = torch.Tensor([[1,2,3,4]])
b = torch.Tensor([[5,6,7,8]])
# 在0方向即在Y方向上拼接
print(torch.cat((a,b),0))
# 在1方向即在X方向上拼接
print(torch.cat((a,b),1))
tensor([[1., 2., 3., 4.],
        [5., 6., 7., 8.]])
tensor([[1., 2., 3., 4., 5., 6., 7., 8.]])

螺旋数据分类

下载绘图函数到本地

!wget https://raw.githubusercontent.com/Atcold/pytorch-Deep-Learning/master/res/plot_lib.py
# 引入重要的库 初始化参数
import random
import torch
from torch import nn, optim
import math
from IPython import display
from plot_lib import plot_data, plot_model, set_default

# 因为colab是支持GPU的,torch 将在 GPU 上运行
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print('device: ', device)

# 初始化随机数种子。神经网络的参数都是随机初始化的,
# 不同的初始化参数往往会导致不同的结果,当得到比较好的结果时我们通常希望这个结果是可以复现的,
# 因此,在pytorch中,通过设置随机数种子也可以达到这个目的
seed = 12345
random.seed(seed)
torch.manual_seed(seed)

N = 1000  # 每类样本的数量
D = 2  # 每个样本的特征维度
C = 3  # 样本的类别
H = 100  # 神经网络里隐层单元的数量
device:  cuda:0

初始化 X 和 Y。 X 可以理解为一个特征矩阵,Y可以理解为样本标签。可以看到,X的为一个 NxC 行, D 列的矩阵。C 类样本,每类样本是 N个,所以是 N*C 行。每个样本的特征维度是2,所以是 2列。

在 python 中,调用 zeros 类似的函数,第一个参数是 y方向的,即矩阵的行;第二个参数是 x方向的,即矩阵的列。

# 样本特征初始化
X = torch.zeros(N * C, D).to(device)
Y = torch.zeros(N * C, dtype=torch.long).to(device)
for c in range(C):
    index = 0
    t = torch.linspace(0, 1, N) # 在[0,1]间均匀的取10000个数,赋给t
    # 下面的代码不用理解太多,总之是根据公式计算出三类样本(可以构成螺旋形)
    # torch.randn(N) 是得到 N 个均值为0,方差为 1 的一组随机数,注意要和 rand 区分开
    inner_var = torch.linspace( (2*math.pi/C)*c, (2*math.pi/C)*(2+c), N) + torch.randn(N) * 0.2
    
    # 每个样本的(x,y)坐标都保存在 X 里
    # Y 里存储的是样本的类别,分别为 [0, 1, 2]
    for ix in range(N * c, N * (c + 1)):
        X[ix] = t[index] * torch.FloatTensor((math.sin(inner_var[index]), math.cos(inner_var[index])))
        Y[ix] = c
        index += 1

print("Shapes:")
print("X:", X.size())
print("Y:", Y.size())
Shapes:
X: torch.Size([3000, 2])
y: torch.Size([3000])
plot_data(X, Y)

构建线性模型分类

# 设置学习率和初始权值
learning_rate = 1e-3
lambda_l2 = 1e-5

# nn 包用来创建线性模型
# 每一个线性模型都包含 weight 和 bias
model = nn.Sequential(
    nn.Linear(D, H),
    nn.Linear(H, C)
)
model.to(device) # 把模型放到GPU上

# nn 包含多种不同的损失函数,这里使用的是交叉熵(cross entropy loss)损失函数
criterion = torch.nn.CrossEntropyLoss()

# 这里使用 optim 包进行随机梯度下降(stochastic gradient descent)优化
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, weight_decay=lambda_l2)

# 开始训练
for t in range(1000):
    # 把数据输入模型,得到预测结果
    y_pred = model(X)
    # 计算损失和准确率
    loss = criterion(y_pred, Y) # 模型的预测结果
    score, predicted = torch.max(y_pred, 1) # 沿着第二个方向(即X方向)提取最大值
    acc = (Y == predicted).sum().float() / len(Y)
    print('[EPOCH]: %i, [LOSS]: %.6f, [ACCURACY]: %.3f' % (t, loss.item(), acc))
    display.clear_output(wait=True)

    # 反向传播前把梯度置 0 
    optimizer.zero_grad()
    # 反向传播优化 
    loss.backward()
    # 更新全部参数
    optimizer.step()
[EPOCH]: 999, [LOSS]: 0.861541, [ACCURACY]: 0.504
print(y_pred.shape) #可以看到模型的预测结果
print(y_pred[10, :])
print(score[10])
print(predicted[10])
torch.Size([3000, 3])
tensor([-0.1566, -0.1720, -0.1466], device='cuda:0', grad_fn=<SliceBackward>)
tensor(-0.1466, device='cuda:0', grad_fn=<SelectBackward>)
tensor(2, device='cuda:0')
# 输出模型
print(model)
plot_model(X, Y, model)
Sequential(
  (0): Linear(in_features=2, out_features=100, bias=True) 
  (1): Linear(in_features=100, out_features=3, bias=True)
)

通过结果可以看出:

  1. 第一层的输入为2(特征维度为主2),输出为100
  2. 第二层的输入为100(上一层的输出),输出为3(类比数)

构建两层神经网络分类

learning_rate = 1e-3
lambda_l2 = 1e-5

# 和上面模型不同的是在两层之间加入了一个 ReLU 激活函数
model = nn.Sequential(
    nn.Linear(D, H),
    nn.ReLU(),
    nn.Linear(H, C)
)
model.to(device)

# 下面的代码和之前是完全一样的
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate, weight_decay=lambda_l2) # built-in L2

# 训练模型,和之前的代码是完全一样的
for t in range(1000):
    y_pred = model(X)
    loss = criterion(y_pred, Y)
    score, predicted = torch.max(y_pred, 1)
    acc = ((Y == predicted).sum().float() / len(Y))
    print("[EPOCH]: %i, [LOSS]: %.6f, [ACCURACY]: %.3f" % (t, loss.item(), acc))
    display.clear_output(wait=True)
    
    # zero the gradients before running the backward pass.
    optimizer.zero_grad()
    # Backward pass to compute the gradient
    loss.backward()
    # Update params
    optimizer.step()
[EPOCH]: 999, [LOSS]: 0.170588, [ACCURACY]: 0.953
# 输出模型
print(model)
plot_model(X, Y, model)
Sequential(
  (0): Linear(in_features=2, out_features=100, bias=True)
  (1): ReLU()
  (2): Linear(in_features=100, out_features=3, bias=True)
)

加入Relu激活函数之后分类的准确率得到了显著的提高:

  1. 采用sigmoid等函数,算激活函数时(指数运算),计算量大,反向传播求误差梯度时,求导涉及除法,计算量相对大,而采用Relu激活函数,整个过程的计算量节省很多

  2. 对于深层网络,sigmoid函数反向传播时,很容易就会出现梯度消失的情况(在sigmoid接近饱和区时,变换太缓慢,导数趋于0,这种情况会造成信息丢失,从而无法完成深层网络的训练

回归分析

下载显示结果所需的文件

!wget https://raw.githubusercontent.com/Atcold/pytorch-Deep-Learning/master/res/plot_lib.py
# 引入重要的库,初始化参数
import random
import torch
from torch import nn, optim
import math
from IPython import display
from plot_lib import plot_data, plot_model, set_default
from matplotlib import pyplot as plt

#初始化
set_default()

# 设置GPU
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

seed = 1
random.seed(seed)
torch.manual_seed(seed)
N = 1000  # 每类样本的数量
D = 1  # 每个样本的特征维度
C = 1  # 类别数
H = 100  # 隐层的神经元数量

# 返回100个1维变量
X = torch.unsqueeze(torch.linspace(-1, 1, 100), dim=1).to(device)
y = X.pow(3) + 0.3 * torch.rand(X.size()).to(device)

print("Shapes:")
print("X:", tuple(X.size()))
print("y:", tuple(y.size()))
Shapes:
X: (100, 1)
y: (100, 1)
# 在坐标系上显示数据
plt.figure(figsize=(6, 6))
# 散点图
plt.scatter(X.cpu().numpy(), y.cpu().numpy())
# 让x轴和y轴单位长度相等
plt.axis('equal');

建立线性模型

# 设置学习率和初始权值
learning_rate = 1e-3
lambda_l2 = 1e-5

# 建立神经网络模型
model = nn.Sequential(
    nn.Linear(D, H),
    nn.Linear(H, C)
)
model.to(device) # 模型转到 GPU

# 对于回归问题,使用MSE损失函数
criterion = torch.nn.MSELoss()

# 定义优化器,使用SGD
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, weight_decay=lambda_l2) # built-in L2

# 开始训练
for t in range(1000):
    # 数据输入模型得到预测结果
    y_pred = model(X)
    # 计算 MSE 损失
    loss = criterion(y_pred, y)
    print("[EPOCH]: %i, [LOSS or MSE]: %.6f" % (t, loss.item()))
    display.clear_output(wait=True)
    # 反向传播前,梯度清零
    optimizer.zero_grad()
    # 反向传播
    loss.backward()
    # 更新参数
    optimizer.step()
[EPOCH]: 999, [LOSS or MSE]: 0.029701
# 展示模型与结果
print(model)
plt.figure(figsize=(6,6))
plt.scatter(X.data.cpu().numpy(), y.data.cpu().numpy())
plt.plot(X.data.cpu().numpy(), y_pred.data.cpu().numpy(), 'r-', lw=5)
plt.axis('equal');
Sequential(
  (0): Linear(in_features=1, out_features=100, bias=True)
  (1): Linear(in_features=100, out_features=1, bias=True)
)

两层神经网络

# 这里定义了2个网络,一个 relu_model,一个 tanh_model,
# 使用了不同的激活函数
relu_model = nn.Sequential(
        nn.Linear(D, H),
        nn.ReLU(),
        nn.Linear(H, C)
)
relu_model.to(device)

tanh_model = nn.Sequential(
        nn.Linear(D, H),
        nn.Tanh(),
        nn.Linear(H, C)   
)
tanh_model.to(device)

# MSE损失函数
criterion = torch.nn.MSELoss()
# 定义优化器,使用 Adam,这里仍使用 SGD 优化器的化效果会比较差,具体原因请自行百度
optimizer_relumodel = torch.optim.Adam(relu_model.parameters(), lr=learning_rate, weight_decay=lambda_l2) 
optimizer_tanhmodel = torch.optim.Adam(tanh_model.parameters(), lr=learning_rate, weight_decay=lambda_l2) 

# 开始训练
for t in range(1000):
    y_pred_relumodel = relu_model(X)
    y_pred_tanhmodel = tanh_model(X)
    # 计算损失与准确率
    loss_relumodel = criterion(y_pred_relumodel, y)
    loss_tanhmodel = criterion(y_pred_tanhmodel, y)
    print(f"[MODEL]: relu_model, [EPOCH]: {t}, [LOSS]: {loss_relumodel.item():.6f}")
    print(f"[MODEL]: tanh_model, [EPOCH]: {t}, [LOSS]: {loss_tanhmodel.item():.6f}")    
    display.clear_output(wait=True)

    optimizer_relumodel.zero_grad()
    optimizer_tanhmodel.zero_grad()
    loss_relumodel.backward()
    loss_tanhmodel.backward()
    optimizer_relumodel.step()
    optimizer_tanhmodel.step()
[MODEL]: relu_model, [EPOCH]: 999, [LOSS]: 0.006479
[MODEL]: tanh_model, [EPOCH]: 999, [LOSS]: 0.007934

从训练结果中可以看到使用Relu激活函数收敛的较快,而Tanh激活函数刚开始收敛速度较慢而随后快速收敛到较好的结果

回归结果

plt.figure(figsize=(12, 6))

def dense_prediction(model, non_linearity):
    plt.subplot(1, 2, 1 if non_linearity == 'ReLU' else 2)
    # 对X轴(-1,1)上的点的数据的维度进行压缩
    X_new = torch.unsqueeze(torch.linspace(-1, 1, 1001), dim=1).to(device)
    with torch.no_grad():
        y_pred = model(X_new)
    # 绘制折线图
    plt.plot(X_new.cpu().numpy(), y_pred.cpu().numpy(), 'r-', lw=1)
    # 绘制散点图
    plt.scatter(X.cpu().numpy(), y.cpu().numpy(), label='data')
    plt.axis('square')
    plt.title(non_linearity + ' models')

dense_prediction(relu_model, 'ReLU')
dense_prediction(tanh_model, 'Tanh')

Relu激活函数是一个分段线性函数,Tanh是一个连续平稳的回归

原因:当输入为负时,Relu的学习速度会变得很慢,甚至使神经元直接无效,因为此时输入小于0,梯度为0,其权重没法更新,因此在剩下的训练过程中会保持静默,所以Tanh的回归函数要相对光滑一些

猜你喜欢

转载自www.cnblogs.com/Qqqqqqgq/p/13377068.html
今日推荐