机器学习及神经网络基础,Pytorch框架

机器学习基础

机器学习的本质:利用数据去解决问题

数据预处理(在深度学习部分很重要)->训练阶段->模型生成->预测阶段

我们通常会选择一部分数据作为测试集,比如20%左右。

有时还会多出一个20%左右的验证集

  • 训练阶段:通过数据训练创建一个预测模型并对其进行微调。
  • 模型生成:预测模型可以从这些数据背后找出答案,帮我们解决某个问题。
  • 预测阶段:通过测试集完成模型评估,从而了解模型在测试集中有效性。

在此过程中,预测模型会被不断改进和使用

再进一步细分,机器学习的步骤为:

收集数据->数据预处理->选择模型->训练->评估->超参数调整->预测

评价机器学习好坏的指标:损失函数loss

实例:预测房价

机器学习及神经网络基础,Pytorch框架_第1张图片

对于“区域”这类特征,需要进行one-hot编码、标签编码等将其进行离散化编码。

对于非数值类型[字符串等等]的数据元,常常使用标签编码将其转化.

比如:海淀、通州、朝阳分别对应着数值1,2,3.

机器学习及神经网络基础,Pytorch框架_第2张图片

机器学习常常有很多特征,基于这些特征,我们需要训练在Model中的权重w,这些特征值构成的特征,称之为权重矩阵Weights,同时还存在着偏biases。

使用线性回归进行房价预测

机器学习及神经网络基础,Pytorch框架_第3张图片

暴力穷举方式:
import numpy as np
import matplotlib.pyplot as plt

#MAE损失函数
def MAE_loss(y,y_hat):
    return np.mean(np.abs(y_hat - y))
#MSE损失函数
def MSE_loss(y,y_hat):
    return np.mean(np.square(y_hat - y))
#线性回归
def linear(x,k,b):
    y = k * x + b
    return y

if __name__ == '__main__':
    x = np.array([50,80,100,200])
    y = np.array([82,119,172,302])
    plt.scatter(x,y)
    #初始化最小损失函数值
    min_loss = float('inf')
    #暴力穷举方式
    for k in np.arange(-2,2,0.1):
        for b in np.arange(-10,10,0.1):
            #计算预测值
            y_hat = [linear(xi,k,b) for xi in list(x)]
            #计算损失函数
            current_mae_loss = MAE_loss(y,y_hat)
            #记录损失函数最小时的k和b
            if current_mae_loss < min_loss:
                min_loss = current_mae_loss
                best_k,best_b = k,b
            #print('mae:{}'.format(current_mae_loss))
    print("best k:{},best b:{}".format(best_k,best_b))

    y_hat = best_k * x + best_b
    plt.plot(x,y_hat,color='red')
    plt.grid()
    plt.show()

结果:

机器学习及神经网络基础,Pytorch框架_第4张图片

梯度下降优化方法:
  1. 批量梯度下降
    • 每次更新时使用所有样本
    • 稳定,收敛慢
  2. 随机梯度下降
    • 每次更新时用1个样本,用1个样本来近似所有的样本
    • 更快收敛,最终解在全局最优解附近
  3. mini-batch梯度下降
    • 每次更新时用b个样本,折中方法
    • 速度较快
#采用批量梯度下降方法
import numpy as np
import matplotlib.pyplot as plt
#平均绝对误差
def MAE_loss(y,y_hat):
    return np.mean(np.abs(y_hat - y))
#均方误差
def MSE_loss(y,y_hat):
    return np.mean(np.square(y_hat - y))

def linear(x,k,b):
    return k * x + b

#gradient of k
def gradient_k(x,y,y_hat):
    n = len(y)
    gradient = 0
    for xi,yi,yi_hat in zip(list(x),list(y),list(y_hat)):
        gradient += (yi_hat - yi) * xi
    return gradient / n

#gradient of b
def gradient_b(y,y_hat):
    n = len(y)
    gradient = 0
    for yi,yi_hat in zip(list(y),list(y_hat)):
        gradient += (yi_hat - yi)
    return gradient / n

if __name__ == '__main__':
    #初始数据
    x = np.array([50,80,100,200])
    y = np.array([82,119,172,302])
    plt.scatter(x,y)
    plt.grid()
    #数据预处理
    max_x = np.max(x)
    max_y = 1
    x = x / max_x
    y = y / max_y
    #初始化参数
    times = 1000 #迭代次数
    min_loss = float('inf') #最小损失函数值
    current_k = 10
    current_b = 10
    learn_rate = 0.1
    #开始迭代
    for i in range(times):
        y_hat = [linear(xi,current_k,current_b) for xi in list(x)]
        current_loss = MSE_loss(y,y_hat)
        if current_loss < min_loss:
            min_loss = current_loss
            best_k,best_b = current_k,current_b
            print('best_k :{},best_b :{}'.format(best_k,best_b))
        #计算梯度
        k_gradient = gradient_k(x,y,y_hat)
        b_gradient = gradient_b(y,y_hat)
        #更新权重
        current_k = current_k - learn_rate * k_gradient
        current_b = current_b - learn_rate * b_gradient

    best_k = best_k / max_x * max_y
    best_b = best_b / max_x * max_y
    print(best_k,best_b)

    x = x * max_x
    y = y * max_y

    plt.plot(x,y_hat,color='red')
    plt.scatter(x,y)
    plt.grid()
    plt.show()

训练过程

机器学习的过程,就是在搜索空间中对w和b进行搜索的过程,使得模型的准确率达到某个标准。一次训练,称为一次迭代,目的就是更新权重和变量,经过不断地迭代,使得模型中的参数不断进行更新,当训练完成时,就可以用该模型对房价进行预测。

机器学习及神经网络基础,Pytorch框架_第5张图片

什么是回归问题?什么是分类问题?

回归问题其实就是实数的求解问题,分类问题是离散问题。

判断方法:输出的数据类型,离散or连续

什么是线性回归,什么是逻辑回归?

线性回归解决的是回归问题。逻辑回归解决的是分类问题,是分类算法[利用sigmod函数]

误差、方差和偏差

loss = bias + variance

误差的期望值 = 噪音的方差 + 模型预测值的方差 + 预测值相对真实值的偏差的平方

机器学习及神经网络基础,Pytorch框架_第6张图片

  • 红色点:测试样本真实值
  • 橙色点:测试样本的真实值 + 噪音
  • 紫色点:每一个具体模型对测试样本进行一次预测,就对应一个点
  • 蓝色点:所有预测值的平均[预测值的期望]
  • 浅蓝色圆圈:预测值的离散程度,即预测值的方差

偏差

偏差反映的是模型再样本上的输出与真实值之间的误差。

如何降低?

  • 增加算法的复杂度
    例如:线性回归增加参数个数来提高模型的复杂度;神经网络通过增加隐单元个数来提高模型的复杂度。
  • 优化输入的特征->增加更多的特征
  • 削弱或者去除已有的正则化约束 => 可能增加方差
  • 调整模型结构

方差

方差越大的模型越容易过拟合。

机器学习及神经网络基础,Pytorch框架_第7张图片

对于方差大,过拟合的情况:

  • 增加训练集的数据量,不会影响偏差。
  • 正则化,模型过于偏向正则项可能影响偏差
  • 使用多种模型训练数据,采用交叉验证等方式选取最终模型

机器学习及神经网络基础,Pytorch框架_第8张图片

正则化:

为了降低模型复杂度。

机器学习的挑战

  • 训练误差,指的是训练集上的误差 => 欠拟合,模型不能在训练集上获得足够的误差
  • 测试误差,指的是测试集上的误差 => 过拟合,训练误差与测试误差之间的差距过大

神经网络基础

机器学习及神经网络基础,Pytorch框架_第9张图片

使用numpy模拟前向传播

机器学习及神经网络基础,Pytorch框架_第10张图片

激活函数

sigmoid

特点:

  • 输入为连续实值,输出结果为0和1之间
  • 负无穷的输出结果为0,正无穷的输出结果为1

机器学习及神经网络基础,Pytorch框架_第11张图片
机器学习及神经网络基础,Pytorch框架_第12张图片

  • 如果初始化神经网络的权值为 [0,1] 之间的随机值,由反向传播算法可知,梯度从后向前传播时,每传递一层梯度值都会减小为原来的0.25倍。如果神经网络层数多,那么梯度在多层传播后将变得很小(接近于0),即梯度消失

  • 当网络权值初始化为 (1,+∞) 区间内的值,则会出现梯度爆炸

tanh:

  • y=tanh(x)是奇函数,即图像过原点并且穿越第一、第三象限的严格单调递增曲线
  • 也称为双曲正切函数,函数曲线与Sigmoid函数相似, 可以通过缩放平移相互变换

机器学习及神经网络基础,Pytorch框架_第13张图片

  • 相比于sigmoid函数,tanh的均值是0
  • tanh相比于Sigmoid函数更容易训练,具有优越性
  • 用于归一化回归问题,其输出在[-1,1]范围内。通常与L2损失一起使用

relu:
机器学习及神经网络基础,Pytorch框架_第14张图片

  • 单侧抑制,ReLU实现稀疏后的模型能够更好地挖掘相关特征,拟合训练数据

  • 对于线性函数而言,ReLU的表达能力更强,尤其体现在深度网络中

  • 而对于非线性函数而言,ReLU由于非负区间的梯度为常数,因此不存在梯度消失问题,使得模型的收敛速度维持在一个稳定状态

综上:

  • sigmoid和tanh函数输出值在(0,1)和(-1,1)之间 => 适合处理概率值;会产生梯度消失 => 不适合深层网络训练
  • relu的有效导数是常数1,解决了深层网络中出现的梯度消失问题 => 更适合深层网络训练

梯度消失

  • 当梯度小于1时,预测值与真实值之间的误差每传播一层会衰减一次,如果在深层模型中使用sigmoid作为激活函数,这种现象尤为明显,将导致模型收敛停滞不前
  • 梯度消失发生时,接近于输出层的隐藏层由于其梯度相对正常,所以权值更新时也就相对正常,但是当越靠近输入层时,由于梯度消失,会导致靠近输入层的隐藏层权值更新缓慢或者停滞 => 这样就导致在训练时,只等价于后面几层的浅层网络的学习

为什么需要在神经网络中使用非线性激活函数?

  • 如果不用激活函数,就相当于激励函数f(x) = x,此时每一层节点的输入都是上层输出的线性函数,那么无论神经网络有多少层,输出都是输入的线性组合 => 与没有隐藏层效果相当
  • 引入非线性函数作为激励函数,这样神经网络表达能力会更加强大 => 不再是输入的线性组合,而是几乎可以逼近任意函数

完整的神经网络学习过程

  1. 定义网络结构(指定输出层、隐藏层、输出层的大小)

  2. 初始化模型参数

  3. 循环操作:

    3.1 执行前向传播

    3.2 计算损失函数

    3.3 执行后向传播

    3.4 权值更新

反向传播

机器学习及神经网络基础,Pytorch框架_第15张图片

权重矩阵的作用

  • 前向传播:传递输入信号
  • 反向传播:传递输出的误差

使用numpy实现一个神经网络

import numpy as np
import matplotlib.pyplot as plt

if __name__ == '__main__':
    #n:样本大小;d_in:输入维度;h:隐藏层维度;d_out:输出维度
    n,d_in,h,d_out = 64,1000,100,10
    #随机生成数据
    x = np.random.randn(n,d_in)  # n行d_in列,具有标准正态分布
    y = np.random.randn(n,d_out) # n行d_out列,具有标准正态分布
    #print(x)
    #随机初始化权重
    w1  = np.random.randn(d_in,h) #输入层到隐藏层权重
    w2  = np.random.randn(h,d_out) #隐藏层到输出层权重
    #设置学习率
    learn_rate = 1e-6
    #迭代次数
    times = 200
    for i in range(times):
        #前向传播 计算预测值y
        temp = x.dot(w1)
        temp_relu = np.maximum(temp,0) #逐元素比较两个array的大小,小于0为0
        y_pred = temp_relu.dot(w2)
        #计算损失函数
        loss = np.square(y_pred - y).sum() / n
        print(i,loss)
        #反向传播
        grad_y_pred = 2.0 * (y_pred - y)
        #计算w2梯度
        grad_w2 = temp_relu.T.dot(grad_y_pred)
        grad_temp_relu = grad_y_pred.dot(w2.T)
        grad_temp = grad_temp_relu.copy()
        #relu激活函数,小于0为0
        grad_temp[temp < 0] = 0
        #计算w1梯度
        grad_w1 = x.T.dot(grad_temp)
        #更新权重
        w1 -= learn_rate * grad_w1
        w2 -= learn_rate * grad_w2
    print(w1,w2)

使用numpy完成基于神经网络的boston房价预测

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_boston
from sklearn.utils import shuffle, resample

# relu函数
def Relu(x):
    res = np.where(x < 0,0,x)
    return res
# 定义损失函数
def MSE_loss(y, y_hat):
    return np.mean(np.square(y_hat - y))
# 定义线性回归函数
def Linear(X, W1, b1):
    y = X.dot(W1) + b1
    return y

if __name__ == '__main__':
    #数据加载
    data = load_boston()
    X_ = data['data']
    y = data['target']
    #将y转化为矩阵的形式
    y = y.reshape(y.shape[0],1) #shape[0]读取第0维长度 reshape:修改为指定维度大小
    #数据规范化 axis = 0:压缩行,对各列求均值,返回1*n的矩阵
    X_ = (X_ - np.mean(X_,axis=0)) / np.std(X_,axis=0)
    """
        初始化网络参数
        定义隐藏层维度n_hidden,w1,b1,w2,b2
    """
    n_features = X_.shape[1] #获取列的维度大小,即特征数
    n_hidden = 10
    w1 = np.random.randn(n_features, n_hidden)
    b1 = np.zeros(n_hidden)
    w2 = np.random.randn(n_hidden, 1)
    b2 = np.zeros(1)
    # 设置学习率
    learning_rate = 1e-6
    # 5000次迭代
    for t in range(5000):
        # 前向传播,计算预测值y (Linear->Relu->Linear)
        l1 = Linear(X_,w1,b1)
        s1 = Relu(l1)
        y_pred = Linear(s1,w2,b2)
        # 计算损失函数, 并输出每次epoch的loss
        loss = MSE_loss(y,y_pred)
        # 反向传播,基于loss 计算w1和w2的梯度
        grad_y_pred = 2 *(y_pred - y)
        grad_w2 = s1.T.dot(grad_y_pred)
        grad_temp_relu = grad_y_pred.dot(w2.T)
        grad_temp_relu[l1 < 0] = 0
        grad_w1 = X_.T.dot(grad_temp_relu)
        # 更新权重, 对w1, w2, b1, b2进行更新
        w1 = w1 - learning_rate * grad_w1
        w2 = w2 - learning_rate * grad_w2
    # 得到最终的w1, w2
    print('w1={} \n w2={}'.format(w1, w2))

使用PyTorch自定义神经网络进行二分类

from sklearn.datasets import make_moons
import torch
import numpy as np
import matplotlib.pyplot as plt
import torch.nn as nn
import torch.nn.functional as F
from sklearn.metrics import accuracy_score

#定义网络模型,继承nn.Module
class Net(nn.Module):
    #初始化
    def __init__(self):
        super(Net,self).__init__()
        #定义第一个FC层,使用Linear transformation
        self.fc1 = nn.Linear(2,100)
        #定义第二个FC层,使用Linear transformation
        self.fc2 = nn.Linear(100,2)
    #前向传播
    def forward(self,x):
        #第一层输出
        x = self.fc1(x)
        #激活层
        x = torch.tanh(x)
        #输出层
        x = self.fc2(x)
        return x
    #预测结果
    def predict(self,x):
        pred = self.forward(x)
        ans = []
        for t in pred:
            if t[0] > t[1]:
                ans.append(0)
            else:
                ans.append(1)
        return torch.tensor(ans)

# 进行预测,并转换为numpy类型
def predict(x):
    x = torch.from_numpy(x).type(torch.FloatTensor)
    ans = model.predict(x)
    return ans.numpy()

# 绘制二分类决策面
def plot_decision_boundary(pred_func, X, y):
    # Set min and max values and give it some padding
    x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5
    y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5
    h = 0.01
    # 计算决策面
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
    # np.c_按行连接两个矩阵,就是把两矩阵左右相加
    Z = pred_func(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    # 绘制分类决策面
    plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral)
    # 绘制样本点
    plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.binary)
    plt.show()


if __name__ == '__main__':
    # 使用make_moon内置生成模型,随机产生二分类数据,200个样本
    np.random.seed(33)
    X,y = make_moons(200,noise=0.2)
    print(y)
    cm = plt.cm.get_cmap('RdYlBu')
    #X[:,0]:所有数组中取第0个数据 X[:,1]:所有数组中取第1个数据
    plt.scatter(X[:,0],X[:,1],s=40,c=y,cmap=cm)
    plt.show()
    #数组转换成张量,且二者共享内存
    X = torch.from_numpy(X).type(torch.FloatTensor)
    y = torch.from_numpy(y).type(torch.LongTensor)
    #初始化模型
    model = Net()
    #定义评估标准
    criterion = nn.CrossEntropyLoss()
    #定义优化器
    print(f'\nParameters: {np.sum([param.numel() for param in model.parameters()])}')
    optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
    #迭代次数
    times = 1000
    #存储每次迭代的loss
    losses = []
    for i in range(times):
        #对输入的x进行预测
        y_pred = model.forward(X)
        #得到损失函数
        loss = criterion(y_pred,y)
        losses.append(loss.item())
        #清空之前的梯度
        optimizer.zero_grad()
        #计算梯度
        loss.backward()
        #调整权重
        optimizer.step()
    print(model.predict(X))
    print(accuracy_score(model.predict(X), y))
    plot_decision_boundary(lambda x: predict(x), X.numpy(), y.numpy())

输出结果:
机器学习及神经网络基础,Pytorch框架_第16张图片
机器学习及神经网络基础,Pytorch框架_第17张图片

你可能感兴趣的:(AI/机器学习,专题,神经网络,pytorch,机器学习)