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

  • 相关阅读:
    【React Native】某个页面禁用物理返回键
    【React Native】DeviceEventEmitter监听通知及带参数传值
    转载【React Native代码】手写验证码倒计时组件
    【React Native】 中设置 APP 名称、应用图标、为安卓添加启动图
    【React Native错误集】* What went wrong: Execution failed for task ':app:installDebug'.
    【React Native错误集】Import fails with "Failed to execute 'ImportScripts' on 'WorkerGlobalScope'"
    【React Native错误集】Android error “Could not get BatchedBridge, make sure your bundle is packaged properly” on start of app
    「React Native笔记」在React的 setState 中操作数组和对象的多种方法(合集)
    【React Native】Error: Attribute application@allowBackup value=(false) from AndroidManifest.xml
    坚果云如何使用二次验证码/谷歌身份验证器/两步验证/虚拟MFA?
  • 原文地址:https://www.cnblogs.com/lipoicyclic/p/13378344.html
Copyright © 2011-2022 走看看