使用梯度下降训练线性模型

使用梯度下降训练线性模型

1 实验内容

**课程内容回顾:**在理论课程中,我们回顾了机器学习的基本概念,模型的评估和选择,线性模
型和广义线性模型的概念和相关的梯度下降的优化方法。

**要求:**不使用Tensorfolow、Pytorch 或者scikit-learn 等机器学习框架,仅使用Numpy,Scipy 和
Matplotlib 等Python 常用科学计算和可视化库,使用梯度下降训练线性模型,实现(两类)正态
分布数据的分类。

2 实验原理

线性分类——最小二乘法

一般来说,线性回归都可以通过最小二乘法求出其方程,可以计算出对于y=bx+a的直线。

一般地,影响y的因素往往不止一个,假设有x1,x2,…,xk,k个因素,通常可考虑如下的线性关系式:

img

logistic回归

logistic回归是一种广义线性回归(generalized linear model),因此与多重线性回归分析有很多相同之处。它们的模型形式基本上相同,都具有 $ wx+b $ ,其中w和b是待求参数,其区别在于他们的因变量不同,多重线性回归直接将w‘x+b作为因变量,即 $ y =wx+b $ ,而logistic回归则通过函数L将 w x + b wx+b wx+b 对应一个隐状态 p = L ( w x + b ) p =L(wx+b) p=L(wx+b) ,然后根据p 与1-p的大小决定因变量的值。如果L是logistic函数,就是logistic回归。

3 具体实现

数据生成与可视化:

使用Numpy生成两类正态分布 N ( ± μ , I d ) , d = 2 , μ = [ 1 ; − 1 ] N(±μ, Id),d = 2,μ = [1; − 1] N(±μ,Id)d=2μ=[1;1],数据总数目m = 500。

这里可以使用多种方法:

  1. numpy.random.multivariate_normal(mean, cov[, size, check_valid, tol]).直接生成。
  2. 先由np.random.randn()生成一维的正态分布,再转化为2维正态。

代码如下所示:

import numpy as np
from numpy.linalg import cholesky
import matplotlib.pyplot as plt
import pandas as pd

sampleNo = 250
mu = np.array([1, -1])
mu2 = np.array([-1, 1])
Sigma = np.array([[1, 0], [0, 1]])
s1 = np.random.multivariate_normal(mu, Sigma, sampleNo)
s1 = np.random.multivariate_normal(mu2, Sigma, sampleNo)

# R = cholesky(Sigma).T
# va, vc = np.linalg.eig(Sigma)
# R2 = (np.diag(va) ** 0.5) @ vc.T
# s1 = np.random.randn(sampleNo, 2) @ R + mu
# s2 = np.random.randn(sampleNo, 2) @ R + mu2

生成数据后,我们使用matplotlib对数据可视化如下:

plt.plot(*s1.T, '.', label='s1')
plt.plot(*s2.T, '.', label='s2')
plt.axis('scaled')
plt.legend()
plt.show()
image-20210924170503518

最后,我们为两类数据打上Label,再打乱后存入csv文件。

data1 = pd.DataFrame(s1)
data1['label'] = 1
data2 = pd.DataFrame(s2)
data2['label'] = -1
data = pd.concat([data1, data2])
data0 = data.sample(frac=1)
data0.to_csv('data0.csv', header=False, index=False)
image-20210924164752801

数据读取与处理:

从csv文件中读取数据,按照8:2的比例划分训练集与测试集,并对数据添加常数1,构造常数项。

def init_data(a = 0.8):
    data = np.loadtxt('data0.csv', delimiter=',')
    index_m = round(data.shape[0]*a)
    traindata = data[:index_m]
    testdata = data[index_m:]
    train_x, train_y = data_p(traindata)
    test_x, test_y = data_p(testdata)

    return train_x, train_y, test_x, test_y

def data_p(dataIn):
    dataMatIn = dataIn[:, 0:-1]
    classLabels = dataIn[:, -1]
    dataMatIn = np.insert(dataMatIn, 0, 1, axis=1)  #特征数据集,添加1是构造常数项x0

    return dataMatIn, classLabels

法一:利用最小二乘求解线性分类

代码为:

def Leastsquare(train_x, train_y):
    # 最小二乘法
    H = train_x
    H0 = np.array(H)
    # H1为H的转置矩阵
    H1 = np.array(H0).T
    H2 = H1 @ H0
    # 求逆矩阵
    H3 = np.linalg.inv(H2)
    # 最终结果计算
    W = H3 @ H1 @ train_y
    # W = grad_descent(train_x, train_y)
    return W

法二:利用线性分类+梯度下降:

代码:

def grad_descent_mse(dataMatIn, classLabels, maxCycle=500):
    dataMatrix = np.mat(dataMatIn)  # (m,n)
    labelMat = np.mat(classLabels).transpose()
    m, n = np.shape(dataMatrix)
    weights = np.ones((n, 1))  # 初始化回归系数(n, 1)
    alpha = 0.001  # 步长
    # 最大循环次数默认为500

    for i in range(maxCycle):
        y_hat = dataMatrix * weights
        weights = weights - alpha * dataMatrix.transpose()*(y_hat - labelMat).reshape(-1,1)
        
    return weights

法三:利用逻辑回归+梯度下降:

随机梯度下降:

使用随机梯度下降与动态调整步长的随机梯度下降。

'''
sigmoid函数
'''
def sigmoid(inX):
    return 1.0 / (1 + np.exp(-inX))


'''
随机梯度下降算法
'''
def stocGradAscent0(dataList, labelList):
    dataArr = np.array(dataList)  # 数据转化为矩阵
    m, n = np.shape(dataArr)
    alpha = 0.01
    weights = np.ones(n)
    for i in range(m):
        h = sigmoid(np.sum(dataArr[i]*weights))
        error = (h-labelList[i])
        weights = weights-alpha*error*dataArr[i]
    return weights

'''
改进的随机梯度下降算法
'''
def stocGradAscent1(dataList, labelList, numIter=150):
    dataArr = np.array(dataList)
    m,n = np.shape(dataArr)
    weights = np.ones(n)
    for j in range(numIter):
        dataIndex = list(range(m))
        for i in range(m):
            alpha = 4/(1.0+j+i)+0.01        # 步长为动态变化
            rand = int(np.random.uniform(0, len(dataIndex)))
            choseIndex = dataIndex[rand]
            h = sigmoid(np.sum(dataArr[choseIndex]*weights))
            error = h-labelList[choseIndex]
            weights = weights-alpha*error*dataArr[choseIndex]
            del(dataIndex[rand])
    return weights

利用逻辑回归模型分类:

该函数以回归系数和特征向量作为输入来计算对应的Sigmoid值,如果Sigmoid值大于0.5则函数返回1,反之返回-1。

def classifyVector(inX, weights):
    prob = sigmoid(np.sum(inX * weights))
    if prob > 0.5:
        return 1
    else:
        return -1

计算分类精度:

def colicTest(trainWeights, testDataList, testLabelList):
    rightCount = 0  # 判断错误的数量
    testCount = len(testDataList)
    for i in range(testCount):
        if int(classifyVector(np.array(testDataList[i]), trainWeights))==int(testLabelList[i]):
            rightCount += 1
    acc = float(rightCount)/testCount
    print("本次的精度为%f" % acc)
    return acc

分类结果可视化:

使用matplotlib对分类边界与分类结果进行可视化:

def plotBestFIt(weights, dataMatIn, classLabels):
    n = np.shape(dataMatIn)[0]
    xcord1 = []
    ycord1 = []
    xcord2 = []
    ycord2 = []
    for i in range(n):
        if classLabels[i] == 1:
            xcord1.append(dataMatIn[i][1])
            ycord1.append(dataMatIn[i][2])
        else:
            xcord2.append(dataMatIn[i][1])
            ycord2.append(dataMatIn[i][2])
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(xcord1, ycord1,s=30, c='red', marker='s')
    ax.scatter(xcord2, ycord2, s=30, c='green')
    x = np.arange(-3, 3, 0.1)
    y = (-weights[0, 0] - weights[1, 0] * x) / weights[2, 0]  #matix
    ax.plot(x, y)
    plt.xlabel('X1')
    plt.ylabel('X2')
    plt.show()

4 结果比较

  1. 最小二乘法求解:

    分类精度与求得W矩阵为:

    image-20210925195507107

    在测试集上的结果可视化:

    image-20210925194932239

    训练集上的分类结果为:

    image-20210925200218839
  2. 线性分类+梯度下降:

    分类精度与求得W矩阵为:

    image-20210925214212462

    在测试集上的结果为:

    image-20210925214344361

    在训练集上的结果为:

    image-20210925214353213
  3. 逻辑回归+梯度下降:

    分类精度与求得W矩阵为:

    image-20210925201046080

    这里我们也确实观察到了二元逻辑回归W的Norm会不断变大的现象!

    在测试集上的结果为:

    image-20210924173726281

    在训练集上的结果为:

    image-20210924173713117
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yuetianw

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值