zoukankan      html  css  js  c++  java
  • 深度学习笔记一

    1. 深度学习笔记一

    1.1 绪论

    (1)关于人工智能、机器学习与深度学习之间的关系:

    人工智能是一种科技领域,分为机器学习,数据挖掘(大概是大数据方向)以及其他方面如作为AL分支的NLP等。对于机器学习,根据有无监督又分为全监督学习(回归算法、朴素贝叶斯以及SVM等),无监督学习(聚类算法,如sklearn的KMeans)以及半监督学习;根据是否应用了神经网络又分为传统机器学习以及神经网络(Neural Network)——深度学习。

    (2)传统机器学习中,特征是人工设计的,在实际应用中,特征往往比分类器更重要。整个过程为原始数据—数据预处理—特征提取—特征转换—预测识别(浅层学习),最终得到结果。Kaggle版Hello World之泰坦尼克号乘客生存预测可以加深这方面的理解。

    (3)传统机器学习 VS 深度学习

    专家系统方法:手工设计程序

    传统机器学习:人基于对问题的理解,手动设计特征。学习的过程,是从手动设计的特征到输出这样一个映射。

    简单的表示学习:没有人工参与,自动学习特征。人工设计的特征不一定能穷尽真实世界的情况,且主观因素会产生影响。

    深度学习:真正有效的特征应该是分层的,深度学习的过程就是从输入到简单特征,再到复杂特征,经过映射得到输出的过程。

    (4)BP算法

    在神经网络里增加了一个隐层,解决了XOR难题,效率比感知器大大提高。反向传播算法把纠错的运算量下降到只和神经元数目成正比。

    (5)DBM & RBM

    算法借用了统计力学中玻尔兹曼分布的概念:一个微粒在某个状态的几率,和那个状态的能量的指数成反比,和它的温度的倒数的指数成反比。使用所谓的受限玻尔兹曼机来学习。RBM相当于一个两层的神经网络,同层神经元不可连接(所以叫“受限”)。深度置信网络DBN就是几层RBM叠加在一起。RBM可以从输入数据进行预先训练,自己发现重要的特征,对神经网络连接的权重进行有效的初始化。被称作:特征提取器或者自动编码器。

    (6)GPU

    工作的核心特点:同时处理海量数据。而GPU在底层的ALU是基于单指令多数据流的架构,善于对大批量数据进行处理。神经网络的计算工作,本质上是大量矩阵运算的操作,适合使用GPU。

    (老黄赶紧发布30系显卡,这样我就能买上一代了T^T)

    (7)深度学习的“不能”

    算法输出不稳定,容易被攻击。两张图像素级别的差别就可能造成识别的错误。

    模型复杂度高,难以纠错和调试 。

    模型层级复合程度高,参数不透明。

    端到端训练方式对数据依赖性强,模型增量性差。

    专注直观感知类问题,对开放性推理问题无能为力。

    人类知识无法有效引入进行监督,机器偏见难以避免(算法必然依赖大数据,但数据不是中立的,从真实社会中抽取必然带有社会固有的不平等、排斥性和歧视)。

    1.2 神经网络基础

    1. 浅层神经网络

    (1)生物神经元:多输入单输出,具有时间整合和空间整合的特性,分兴奋性输出和抑制性输入两种类型,具有阈值特性。

    (2)M-P神经元:对生物神经元的抽象和简化。多输入信号进行累加(Sigma_i x_i)

    权值(omega_i)正负模拟兴奋或抑制,大小模拟强度。输入和超过阈值( heta),神经元被激活(fire)。

    输出为(y=f(Sigma omega_i x_i - heta))。有时看到的写法往往是(omega^T x),看不到( heta),可以这么想:把(omega_0)看作(- heta)(x_0)看作1,就是对上式更简洁的描述了。

    为什么需要激活函数呢?对于激活函数(f),神经元继续传递信息、产生新连接的概率(超过阈值被激活,但不一定传递,比如闹钟响了我还有可能赖床2333)。如果没有激活函数,就相当于矩阵连乘,(x^T W_1 ... W_n = x^T Pi W_k),多层和一层一样,只能拟合线性函数。

    (3)常见的激活函数:

    线性函数(如 恒等函数):(f(x)=kx+c)

    S性函数:(sigma(z)= frac{1}{1+e^-z}) (sigma(z)'= sigma(z)(1-sigma(z))) 问题:容易饱和且输出不对称。

    (4)单层感知器

    M-P神经元的权重预先设置,无法学习。单层感知器是首个可以学习的人工神经网络。

    逻辑非:(h_Theta(x)=g(10-20x_1)) 逻辑或:(h_Theta(x)=g(-10+20x_1+20x_2))

    (5)多层感知器

    可以实现同或门,解决非线性问题。

    (6)万有逼近定理

    如果一个隐层包含足够多的神经元,三层前馈神经网络(输入-隐层-输出)能够任意精度逼近任意预定的连续函数。

    为什么线性分类任务组合后可以解决非线性分类任务?第二层感知器看到的其实不是原始的数据分布,看到的是被第一层感知器进行空间变换后的分布。

    当隐层足够宽时,双隐层感知器(输入-隐层1-隐层2-输出)可以逼近任意非连续函数,可以解决任何复杂的分类问题。

    (7)神经网络每一层的作用

    每一层数学公式:(vec{y}=a(Wvec{x}+b))

    完成输入->输出空间变换,包括:

    线性变换(Wvec{x}):升/降维,放大/缩小,旋转。

    (+b):平移

    (a):弯曲

    训练数据就是让神经网络去选择这样一个线性或者非线性的变换。

    神经网络学习如何利用矩阵的线性变换加激活函数的非线性变换,将原始输入空间投影到线性可分的空间去分类/回归。

    增加节点数:增加维度,即增加线性转换能力。

    增加层数:增加激活函数的次数,即增加非线性转换次数。

    (8)更宽 or 更深?

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

    宽度和深度对函数复杂度的贡献是不同的,深度的贡献是指数增长,宽度的贡献是线性的。

    (9)神经网络的参数学习:误差反向传播

    多层神经网络可看成是一个复合的非线性多元函数。给定训练数据({x^i,y^i}),希望损失

    (Sigma_i loss(F(x^i),y^i))尽可能小。

    无约束优化:梯度下降

    参数沿负梯度方向更新可以使函数值下降(通过泰勒展开可证明)。

    ( heta_j= heta_j-alphafrac{partial }{partial heta_j}J( heta))

    三层前馈神经网络的BP算法:

    前馈为(z[2]=W[2]*a[1])

    反馈为(da[1]=W[2]*dz[2])

    残差为损失函数在某个节点的偏导数。

    (10)深度学习开发框架

    为什么要基于PyTorch?涨势迅猛,稳坐榜眼。相比Tensorflow更友好。

    (11)深层神经网络的问题:梯度消失

    Sigmoid激活函数求导后在(sigma(z)=frac{1}{2})处最大为(frac{1}{4}),取其他值时更小,这样反向传播时有很多个节点。就会造成梯度消失这样的问题。

    增加深度会造成梯度消失(gradient vanishing),误差无法传播。多层网络容易陷入局部极值,难以训练。因此三层神经网络是主流。同时预训练和新激活函数使深度成为可能。

    2. 深层神经网络

    (1)逐层预训练(layer-wise- pre-training)

    问题一:网络层数越多,局部极小值就越多,有可能网络收敛到很差的局部极小值里。

    问题二:只能更新后面几层的参数。

    解决:找到一个还不错的初始值,即权重初始化。

    每次选择一层进行逐层预训练,最后再从上往下进行一次微调(fine-tuning)

    (2)受限玻尔兹曼机和自编码器

    逐层预训练是看不到输出的,应该怎么计算参数呢?

    自编码器(autoencoder)假设输入与输出相同(target=input),是一种尽可能复现输入信号的神经网络。将input输入一个encoder编码器,就会得到一个code;加一个decoder解码器输出信息。通过调整encoder和decoder的参数,使得重构误差最小。没有额外监督信息:无标签数据,误差的来源是直接重构后信号与原输入相比得到。

    受限玻尔兹曼机(RBM)是两层的神经网络,包含可见层v(输入层)和隐藏层h。不同层之间全连接,层内无连接,是二分图(匈牙利算法和二分图最大独立集啥的还没学QAQ)。与感知器不同,RBM没有显式的重构过程。从联合概率到条件概率。

    自编码器 VS 受限玻尔兹曼机

    Python中的图像处理

    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)
    

    可以看到colony的类型是numpy的ndarray(多维容器)。

    在这里Image读出来的是PIL的类型,而skimage.io读出来的数据是numpy格式的。

    # Plot all channels of a real image
    plt.subplot(121)
    plt.imshow(colony[:,:,:])
    plt.title('3-channel image')
    plt.axis('off')
    
    # Plot one channel only
    plt.subplot(122)
    plt.imshow(colony[:,:,0])
    plt.title('1-channel image')
    plt.axis('off');
    

    matplotlib在大数据的课上学过了,就不赘述了。

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

    # Get the pixel value at row 10, column 10 on the 10th row and 20th column
    camera = data.camera()
    print(camera[10, 20])
    
    # Set a region to black
    camera[30:100, 10:100] = 0
    plt.imshow(camera, 'gray')
    

    设置了一个区域为灰色。

    skimage.data.camera()即灰度相机图像,data自带的。

    # Set the first ten lines to black
    camera = data.camera()
    camera[:10] = 0
    plt.imshow(camera, 'gray')
    

    设置前十行为灰色。

    # Set to "white" (255) pixels where mask is True
    camera = data.camera()
    mask = camera < 80
    camera[mask] = 255
    plt.imshow(camera, 'gray')
    

    这波是黑白反转。

    # Change the color for real images
    cat = data.chelsea()
    plt.imshow(cat)
    

    展示一下猫,这也是自带的。

    # Set brighter pixels to red
    red_cat = cat.copy()
    reddish = cat[:, :, 0] > 160
    red_cat[reddish] = [255, 0, 0]
    plt.imshow(red_cat)
    

    把>160的高亮地方的像素设置为红色。

    # Change RGB color to BGR for openCV
    BGR_cat = cat[:, :, ::-1]
    plt.imshow(BGR_cat)
    

    改变颜色。

    3. 转换图像数据类型

    • 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.

    from skimage import img_as_float, img_as_ubyte
    float_cat = img_as_float(cat)
    uint_cat = img_as_ubyte(float_cat)
    

    skimage中,图片用numpy的数组来存储。float的范围是-1~1。

    4. 显示图像直方图

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

    numpy的ravel()将多维数组降至一维。

    5. 图像分割

    # Use colony image for segmentation
    colony = io.imread('yeast_colony_array.jpg')
    
    # Plot histogram
    img = skimage.color.rgb2gray(colony)
    plt.hist(img.ravel(), bins=256, histtype='step', color='black');
    
    # Use thresholding
    plt.imshow(img>0.5)
    

    使用阈值(作用为降噪...?)

    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')

    Canny算子是目前找到的一个最优的边缘检测算子。步骤的话balabala

    貌似是第一张图显示边缘,第二张图显示填充后的?

    7. 改变图像对比度

    # Load an example image
    img = data.camera()
    plt.imshow(img, 'gray')
    
    # Contrast stretching
    p2, p98 = np.percentile(img, (2, 98))
    img_rescale = exposure.rescale_intensity(img, in_range=(p2, p98))
    plt.imshow(img_rescale, 'gray')
    

    这里会出现一个NameError: name 'exposure' is not defined,原因是缺少from skimage import exposure。

    percentile(img, (2, 98))在python中计算一个多维数组的任意百分比分位数,此处的百分位是从小到大排列,只需用np.percentile即可。这里的写法是解包了..?

    skimage.exposure.exposure 模块中的函数,在对图像进行拉伸或者伸缩强度水平后返回修改后的图像,

    rescale_intensity(image, in_range=’image’, out_range=’dtype’)

    输入图像和输出图像的强度范围分别由in_range 和out_range指定,用来拉伸或缩小输入图像的强度范围。

    # Equalization
    img_eq = exposure.equalize_hist(img)
    plt.imshow(img_eq, 'gray')
    

    这里是对直方图均衡化。

    # Adaptive Equalization
    img_adapteq = exposure.equalize_adapthist(img, clip_limit=0.03)
    plt.imshow(img_adapteq, 'gray')
    

    另一个直方图均衡化函数,有限对比度自适应直方图均衡。clip_limit为剪切极限,归一化在0和1之间(更高的值给出更多的对比度)。

    # Display results
    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()
    
        # Display image
        ax_img.imshow(img, cmap=plt.cm.gray)
        ax_img.set_axis_off()
        ax_img.set_adjustable('box')
    
        # Display histogram
        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([])
    
        # Display cumulative distribution
        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学习笔记

    1. 定义数据

    一般定义数据使用torch.Tensor , tensor的意思是张量,是数字各种形式的总称

    import torch
    # 可以是一个数
    x = torch.tensor(666)
    print(x)
    
    # 可以是一维数组(向量)
    x = torch.tensor([1,2,3,4,5,6])
    print(x)
    

    张量是数字各种形式的总称。

    # 可以是二维数组(矩阵)
    x = torch.ones(2,3)
    print(x)
    
    
    # 可以是任意维度的数组(张量)
    x = torch.ones(2,3,4)
    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.]]])

    Tensor支持各种各样类型的数据,包括:

    torch.float32, torch.float64, torch.float16, torch.uint8, torch.int8, torch.int16, torch.int32, torch.int64 。这里不过多描述。

    创建Tensor有多种方法,包括:ones, zeros, eye, arange, linspace, rand, randn, normal, uniform, randperm, 使用的时候可以在线搜,下面主要通过代码展示。

    # 创建一个空张量
    x = torch.empty(5,3)
    print(x)
    
    # 创建一个随机初始化的张量
    x = torch.rand(5,3)
    print(x)
    
    # 创建一个全0的张量,里面的数据类型为 long
    x = torch.zeros(5,3,dtype=torch.long)
    print(x)
    
    # 基于现有的tensor,创建一个新tensor,
    # 从而可以利用原有的tensor的dtype,device,size之类的属性信息
    y = x.new_ones(5,3)   #tensor new_* 方法,利用原来tensor的dtype,device
    print(y)
    
    z = torch.randn_like(x, dtype=torch.float)    # 利用原来的tensor的大小,但是重新定义了dtype
    print(z)
    

    2. 定义操作

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

    最终,还是需要用Tensor来进行计算的,计算无非是

    • 基本运算,加减乘除,求幂求余
    • 布尔运算,大于小于,最大最小
    • 线性运算,矩阵乘法,求模,求行列式

    基本运算包括: 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 等

    # 创建一个 2x4 的tensor
    m = torch.Tensor([[2, 5, 3, 7],
                      [4, 2, 1, 9]])
    
    print(m.size(0), m.size(1), m.size(), sep=' -- ')
    

    显示每一个维度的size。

    # 返回 m 中元素的数量
    print(m.numel())
    # 返回 第0行,第2列的数
    print(m[0][2])
    # 返回 第1列的全部元素
    print(m[:, 1])
    # 返回 第0行的全部元素
    print(m[0, :])
    # Create tensor of numbers from 1 to 5
    # 注意这里结果是1到4,没有5
    v = torch.arange(1, 5)
    print(v)
    # Scalar product
    m @ v
    # Calculated by 1*2 + 2*5 + 3*3 + 4*7
    m[[0], :] @ v
    # Add a random tensor of size 2x4 to m
    m + torch.rand(2, 4)
    # 转置,由 2x4 变为 4x2
    print(m.t())
    # 使用 transpose 也可以达到相同的效果,具体使用方法可以百度
    print(m.transpose(0, 1))
    # returns a 1D tensor of steps equally spaced points between start=3, end=8 and steps=20
    torch.linspace(3, 8, 20)
    #输出为tensor([3.0000, 3.2632, 3.5263, 3.7895, 4.0526, 4.3158, 4.5789, 4.8421, 5.1053,5.3684, 5.6316, 5.8947, 6.1579, 6.4211, 6.6842, 6.9474, 7.2105, 7.4737,7.7368, 8.0000])
    from matplotlib import pyplot as plt
    
    # matlabplotlib 只能显示numpy类型的数据,下面展示了转换数据类型,然后显示
    # 注意 randn 是生成均值为 0, 方差为 1 的随机数
    # 下面是生成 1000 个随机数,并按照 100 个 bin 统计直方图
    plt.hist(torch.randn(1000).numpy(), 100);
    #注意上面转换为numpy的方法
    # 当数据非常非常多的时候,正态分布会体现的非常明显
    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 方各上拼接), 会得到 2x4 的矩阵
    print( torch.cat((a,b), 0))
    # 在 1 方向拼接 (即在 X 方各上拼接), 会得到 1x8 的矩阵
    print( torch.cat((a,b), 1))
    

    螺旋数据分类

    如同课程视频里那个例子,对于螺旋数据需要对空间进行变换。

    !wget https://raw.githubusercontent.com/Atcold/pytorch-Deep-Learning/master/res/plot_lib.py
    

    首先是下载数据,然后import基本的库,对参数初始化。

    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 = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

    结果小车GPU有问题,疯狂报错T^T。

    设置种子是对随机化的状态记录,很巧妙。

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

    在 python 中,调用 zeros 类似的函数,第一个参数是 y方向的,即矩阵的行;第二个参数是 x方向的,即矩阵的列,大家得注意下,不要搞反了。下面结合代码看看 3000个样本的特征是如何初始化的。

    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())
    # visualise the data
    plot_data(X, Y)
    

    1. 构建线性模型进行分类

    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)
        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()
    

    这里对上面的一些关键函数进行说明:

    使用 print(y_pred.shape) 可以看到模型的预测结果,为[3000, 3]的矩阵。每个样本的预测结果为3个,保存在 y_pred 的一行里。值最大的一个,即为预测该样本属于的类别

    score, predicted = torch.max(y_pred, 1) 是沿着第二个方向(即X方向)提取最大值。最大的那个值存在 score 中,所在的位置(即第几列的最大)保存在 predicted 中。下面代码把第10行的情况输出,供解释说明

    此外,大家可以看到,每一次反向传播前,都要把梯度清零,参考:https://www.zhihu.com/question/303070254

    运行结果:[EPOCH]: 999, [LOSS]: 0.861541, [ACCURACY]: 0.504

    print(y_pred.shape)
    print(y_pred[10, :])
    print(score[10])
    print(predicted[10])
    
    # Plot trained model
    print(model)
    plot_model(X, Y, model)
    

    上面使用 print(model) 把模型输出,可以看到有两层:

    • 第一层输入为 2(因为特征维度为主2),输出为 100;
    • 第二层输入为 100 (上一层的输出),输出为 3(类别数)

    从上面图示可以看出,线性模型的准确率最高只能达到 50% 左右,对于这样复杂的一个数据分布,线性模型难以实现准确分类。

    2. 构建两层神经网络分类

    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.213117, [ACCURACY]: 0.926

    # Plot trained model
    print(model)
    plot_model(X, Y, model)
    

    可以看到分类效果较好,关键在于加入了ReLU激活函数。ReLU函数速度快精度高,逐渐取代了Sigmoid函数。

    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()

    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 # 隐层的神经元数量

    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()))

    在坐标系上显示数据

    plt.figure(figsize=(6, 6))
    plt.scatter(X.cpu().numpy(), y.cpu().numpy())
    plt.axis('equal');

  • 相关阅读:
    十进制数转换
    桶排序
    快速排序
    单词倒排
    (c++) string b; cin>>b; int i=strlen(b); 报错的原因。
    n的阶乘(1<n<10000)(结果超大……)
    2020软件工程最后一次作业
    2020软件工程第四次作业
    2020软件工程第三次作业
    2020软件工程第二次作业
  • 原文地址:https://www.cnblogs.com/lipoicyclic/p/13378344.html
Copyright © 2011-2022 走看看