机器学习03:使用logistic回归方法解决猫狗分类问题

机器学习03:使用logistic回归方法解决猫狗分类问题

前言

Logistic回归方法,是最经典的机器学习方法。即使在今天,深度学习神经网络,他的算法核心也依旧包含logistic回归思想。在本篇博客中我会详细介绍他的算法原理,以及使用随机梯度下降法来求解优化问题,最终我们利用该算法来解决一个经典图像分类问题:猫狗数据集并利用数据验证集评估结果。本篇博客代码已开源🤗:liujiawen-jpg/Logistic-Regres: logiti回归方法用于解决猫狗数据集分类 (github.com),需要可上github自取,如果对你有帮助的话劳烦点个星。

1. Logistic回归原理

1.1 从线性回归说起

我们假设我们想有一堆数据
D = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , … , ( x m , y m ) } D=\left\{\left(\boldsymbol{x}_{1}, y_{1}\right),\left(\boldsymbol{x}_{2}, y_{2}\right), \ldots,\left(\boldsymbol{x}_{m}, y_{m}\right)\right\} D={(x1,y1),(x2,y2),,(xm,ym)}

假设y与x存在关系:
f ( x i ) = w x i + b , 使得  f ( x i ) ≃ y i .  f\left(x_{i}\right)=w x_{i}+b \text {, 使得 } f\left(x_{i}\right) \simeq y_{i} \text {. } f(xi)=wxi+b使得 f(xi)yi
我们想通过这样的数据去寻找w,b的值那么如何去做呢?可以这样做,假设我们为了让未知的参数{w,b}变成已知的参数。我们可以随便假设上千个或者上万个w,b对于所有的数据我们都用这些候选的w,b去测试误差值,最终哪组的w,b的误差值最小我们就认为这一组{w,b}作为最终的值。

通过这样我们就把原问题转化为寻找w,b代入函数式使得与提供的真值yi的误差最小(这里我们误差使用均方差可以消除负数的影响):
( w ∗ , b ∗ ) = arg ⁡ min ⁡ ( w , b ) ∑ i = 1 m ( f ( x i ) − y i ) 2 = arg ⁡ min ⁡ ( w , b ) ∑ i = 1 m ( y i − w x i − b ) 2 \begin{aligned} \left(w^{*}, b^{*}\right) &=\underset{(w, b)}{\arg \min } \sum_{i=1}^{m}\left(f\left(x_{i}\right)-y_{i}\right)^{2} \\ &=\underset{(w, b)}{\arg \min } \sum_{i=1}^{m}\left(y_{i}-w x_{i}-b\right)^{2} \end{aligned} (w,b)=(w,b)argmini=1m(f(xi)yi)2=(w,b)argmini=1m(yiwxib)2

这里利用均方误差最小化来求解参数的方法称为最小二乘法。那么如何求解呢?熟悉高等数学的同学肯定就知道了,对于一个函数我们求他的极值可以对他求导来求取极值点从而求得极值。那么这里又有一个问题,极值有极大值和极小值之分,为什么我们这么确定我们求取的一定是极小值呢。其实这个问题早在几十年前就已经被解决了,这个函数被学者严格证明过为凸函数(关于这一点可以查看该博客博主写的很详细【机器学习算法】基于最小二乘损失(MSE)的多元线性回归解析解推导_SESESssss的博客-CSDN博客)而由于凸函数的性质我们求取极值点求取出来的一定是极小值。

那么我们拥有两个未知量w,b根据多元函数知识,我们需要分别对这两个参数求偏导数,并且令这两个偏导数为0.从而求取极值点:
∂ E ( w , b ) ∂ w = 2 ( w ∑ i = 1 m x i 2 − ∑ i = 1 m ( y i − b ) x i ) ∂ E ( w , b ) ∂ b = 2 ( m b − ∑ i = 1 m ( y i − w x i ) ) \begin{aligned} &\frac{\partial E_{(w, b)}}{\partial w}=2\left(w \sum_{i=1}^{m} x_{i}^{2}-\sum_{i=1}^{m}\left(y_{i}-b\right) x_{i}\right) \\ &\frac{\partial E_{(w, b)}}{\partial b}=2\left(m b-\sum_{i=1}^{m}\left(y_{i}-w x_{i}\right)\right) \end{aligned} wE(w,b)=2(wi=1mxi2i=1m(yib)xi)bE(w,b)=2(mbi=1m(yiwxi))
通过令这两个导数为0,我们最终可以求出w,b的表达式:
w = ∑ i = 1 m y i ( x i − x ˉ ) ∑ i = 1 m x i 2 − 1 m ( ∑ i = 1 m x i ) 2 b = 1 m ∑ i = 1 m ( y i − w x i ) \begin{gathered} w=\frac{\sum_{i=1}^{m} y_{i}\left(x_{i}-\bar{x}\right)}{\sum_{i=1}^{m} x_{i}^{2}-\frac{1}{m}\left(\sum_{i=1}^{m} x_{i}\right)^{2}} \\ b=\frac{1}{m} \sum_{i=1}^{m}\left(y_{i}-w x_{i}\right) \end{gathered} w=i=1mxi2m1(i=1mxi)2i=1myi(xixˉ)b=m1i=1m(yiwxi)

我们观察,w,b的表达式可以看出,在这些表达式里没有一个是未知量无论是y还是x还是x的平均值,这些都是已知的值,而这个累计加和也只需要使用计算机编程语言循环累加即可实现。那么通过这样我们就利用已知的数据量解决了这些未知的数据问题。

但是在这个问题我们输入的x只是一维的,可不可以将我们的数据规模扩大到多维情况下?

1.2 多元线性回归问题

假设我们现在给定由n个属性描述的示例
x = ( x 1 ; x 2 ; x 3 ; . . . x d ) x= (x_1;x_2;x_3;...x_d) x=(x1;x2;x3;...xd)
我们试图学得一个通过属性的线性组合来进行预测的函数,
f ( x i ) = w T x i + b ,  使得  f ( x i ) ≃ y i f\left(\boldsymbol{x}_{i}\right)=\boldsymbol{w}^{\mathrm{T}} \boldsymbol{x}_{i}+b, \text { 使得 } f\left(\boldsymbol{x}_{i}\right) \simeq y_{i}\\ f(xi)=wTxi+b, 使得 f(xi)yi

f ( x ) = w 1 x 1 + w 2 x 2 + … + w d x d + b  也可以用向量的形式表示成  f ( x ) = w T x + b \begin{aligned} &f(\boldsymbol{x})=w_{1} x_{1}+w_{2} x_{2}+\ldots+w_{d} x_{d}+b \\ &\text { 也可以用向量的形式表示成 } \\ &\qquad f(\boldsymbol{x})=\boldsymbol{w}^{\mathrm{T}} \boldsymbol{x}+b \end{aligned} f(x)=w1x1+w2x2++wdxd+b 也可以用向量的形式表示成 f(x)=wTx+b
那么现在我们的问题就变成了如何确定多维向量 w T w^T wT​,与b的问题了,这个就是著名的多元线性回归问题。同样的我们依然可以利用上面的方法来解决多元线性回归问题,但是为了便于接下来的计算,我们利用线性代数知识将w与b合并为一个矩阵[w,b],同时给相应x矩阵添加值全为1的一列从而消除影响:
X = ( x 11 x 12 … x 1 d 1 x 21 x 22 … x 2 d 1 ⋮ ⋮ ⋱ ⋮ ⋮ x m 1 x m 2 … x m d 1 ) = ( x 1 T 1 x 2 T 1 ⋮ ⋮ x m T 1 ) \mathbf{X}=\left(\begin{array}{ccccc} x_{11} & x_{12} & \ldots & x_{1 d} & 1 \\ x_{21} & x_{22} & \ldots & x_{2 d} & 1 \\ \vdots & \vdots & \ddots & \vdots & \vdots \\ x_{m 1} & x_{m 2} & \ldots & x_{m d} & 1 \end{array}\right)=\left(\begin{array}{cc} \boldsymbol{x}_{1}^{\mathrm{T}} & 1 \\ \boldsymbol{x}_{2}^{\mathrm{T}} & 1 \\ \vdots & \vdots \\ \boldsymbol{x}_{m}^{\mathrm{T}} & 1 \end{array}\right) X=x11x21xm1x12x22xm2x1dx2dxmd111=x1Tx2TxmT111

这样我们的在上面的公式就可以化成下面这个公式: w ^ ∗ = arg ⁡ min ⁡ w ^ ( y − X w ^ ) T ( y − X w ^ ) \hat{\boldsymbol{w}}^{*}=\underset{\hat{\boldsymbol{w}}}{\arg \min }(\boldsymbol{y}-\mathbf{X} \hat{\boldsymbol{w}})^{\mathrm{T}}(\boldsymbol{y}-\mathbf{X} \hat{\boldsymbol{w}}) w^=w^argmin(yXw^)T(yXw^)

跟上面不一样的是,对于这里我们求取极值只需求一次导数,并令该导数为0计算极值点w ∂ E w ^ ∂ w ^ = 2 X T ( X w ^ − y ) \frac{\partial E_{\hat{\boldsymbol{w}}}}{\partial \hat{\boldsymbol{w}}}=2 \mathbf{X}^{\mathrm{T}}(\mathbf{X} \hat{\boldsymbol{w}}-\boldsymbol{y}) w^Ew^=2XT(Xw^y)
∂ E w ^ ∂ w ^ = 2 X T ( X w ^ − y ) w ^ ∗ = ( X T X ) − 1 X T y \frac{\partial E_{\hat{\boldsymbol{w}}}}{\partial \hat{\boldsymbol{w}}}=2 \mathbf{X}^{\mathrm{T}}(\mathbf{X} \hat{\boldsymbol{w}}-\boldsymbol{y})\\ \hat{\boldsymbol{w}}^{*}=\left(\mathbf{X}^{\mathrm{T}} \mathbf{X}\right)^{-1} \mathbf{X}^{\mathrm{T}} \boldsymbol{y} w^Ew^=2XT(Xw^y)w^=(XTX)1XTy
通过这样的方式我们仍然可以利用给定的已知量计算出最合适的w值。

1.3 对数线性回归

那么接下来就到我们的重头戏了,到底什么是logistic回归呢?这里我们需要先知道什么是logistic函数。该函数其实又称sigmoid函数在现在深度学习激活函数中也经常使用,他的函数表达式是:
y = 1 1 + e − z y=\frac{1}{1+e^{-z}} y=1+ez1
这是一个阶跃函数:

在这里插入图片描述

可以满足我们对于二分类的需求。同时该函数由于含有一个非常特殊的指数函数(该函数是可以无限微分的函数),他也可以满足神经网络中多次求导的需求。所以该函数即使是在提出后几十年的今天在深度学习中也依然广泛使用。

那么对于二分类问题这样的非线性问题,拟合直线
f ( x i ) = w T x i + b ,  使得  f ( x i ) ≃ y i f\left(\boldsymbol{x}_{i}\right)=\boldsymbol{w}^{\mathrm{T}} \boldsymbol{x}_{i}+b, \text { 使得 } f\left(\boldsymbol{x}_{i}\right) \simeq y_{i}\\ f(xi)=wTxi+b, 使得 f(xi)yi
效果就会变得很差,所以我们转而对 f ( x ) f(x) f(x)进行一次映射使用一些激活函数(relu,swish,sigmoid等) 使得问题变成 y i = G ( f ( x ) ) , G ( x ) 为 激 活 函 数 y_i = G(f(x)),G(x)为激活函数 yi=G(f(x)),G(x),通过这样的方法我们就可以仍然通过一个线性函数来拟合一些非线性问题。那么使用sigmoid函数后下如何求解 w , b w,b w,b呢?这里由于公式推导过多我这里就直接使用我在草稿纸上的推导过程来说明了:

在这里插入图片描述

通过上述一系列的推导,我们得出求解β的公式: ℓ ( β ) = ∑ i = 1 m ( − y i β T x ^ i + ln ⁡ ( 1 + e β T x ^ i ) ) β ∗ = arg ⁡ min ⁡ β ℓ ( β ) \ell(\boldsymbol{\beta})=\sum_{i=1}^{m}\left(-y_{i} \boldsymbol{\beta}^{\mathrm{T}} \hat{\boldsymbol{x}}_{i}+\ln \left(1+e^{\boldsymbol{\beta}^{\mathrm{T}} \hat{\boldsymbol{x}}_{i}}\right)\right)\\\boldsymbol{\beta}^{*}=\underset{\boldsymbol{\beta}}{\arg \min } \ell(\boldsymbol{\beta}) (β)=i=1m(yiβTx^i+ln(1+eβTx^i))β=βargmin(β)

这个问题依然是凸函数求最小值优化问题,我们对此可以使用梯度下降法或者牛顿法来求解。如梯度下降法使用的是一阶导数来解决问题,他倡导的是使用一个超参数学习率α来控制我们变量每次调整的方向,慢慢地趋近可以使得全局最优的方向,他的第t+1轮参数迭代公式是:
β t + 1 = β t − α ∂ ℓ ( β ) ∂ β ∂ ℓ ( β ) ∂ β = − ∑ i = 1 m x ^ i ( y i − p 1 ( x ^ i ; β ) ) \boldsymbol{\beta}^{t+1}=\boldsymbol{\beta}^{t}-α\frac{\partial \ell(\boldsymbol{\beta})}{\partial \boldsymbol{\beta}}\\\frac{\partial \ell(\boldsymbol{\beta})}{\partial \boldsymbol{\beta}}=-\sum_{i=1}^{m} \hat{\boldsymbol{x}}_{i}\left(y_{i}-p_{1}\left(\hat{\boldsymbol{x}}_{i} ; \boldsymbol{\beta}\right)\right) βt+1=βtαβ(β)β(β)=i=1mx^i(yip1(x^i;β))
梯度下降法由于只需要求解一阶导,他的计算量更小运算速度更快,在深度学习神经网络中广泛使用。但是这样也会不可避免地使得某些问题陷入局部最优的情况。而牛顿法需要使用求解二阶导,他的计算量更大,但是计算结果更加准确。他的第t+1轮参数迭代公式是:
β t + 1 = β t − ( ∂ 2 ℓ ( β ) ∂ β ∂ β T ) − 1 ∂ ℓ ( β ) ∂ β ∂ ℓ ( β ) ∂ β = − ∑ i = 1 m x ^ i ( y i − p 1 ( x ^ i ; β ) ) ∂ 2 ℓ ( β ) ∂ β ∂ β T = ∑ i = 1 m x ^ i x ^ i T p 1 ( x ^ i ; β ) ( 1 − p 1 ( x ^ i ; β ) ) \boldsymbol{\beta}^{t+1}=\boldsymbol{\beta}^{t}-\left(\frac{\partial^{2} \ell(\boldsymbol{\beta})}{\partial \boldsymbol{\beta} \partial \boldsymbol{\beta}^{\mathrm{T}}}\right)^{-1} \frac{\partial \ell(\boldsymbol{\beta})}{\partial \boldsymbol{\beta}}\\\begin{aligned} \frac{\partial \ell(\boldsymbol{\beta})}{\partial \boldsymbol{\beta}} &=-\sum_{i=1}^{m} \hat{\boldsymbol{x}}_{i}\left(y_{i}-p_{1}\left(\hat{\boldsymbol{x}}_{i} ; \boldsymbol{\beta}\right)\right) \\ \frac{\partial^{2} \ell(\boldsymbol{\beta})}{\partial \boldsymbol{\beta} \partial \boldsymbol{\beta}^{\mathrm{T}}} &=\sum_{i=1}^{m} \hat{\boldsymbol{x}}_{i} \hat{\boldsymbol{x}}_{i}^{\mathrm{T}} p_{1}\left(\hat{\boldsymbol{x}}_{i} ; \boldsymbol{\beta}\right)\left(1-p_{1}\left(\hat{\boldsymbol{x}}_{i} ; \boldsymbol{\beta}\right)\right) \end{aligned} βt+1=βt(ββT2(β))1β(β)β(β)ββT2(β)=i=1mx^i(yip1(x^i;β))=i=1mx^ix^iTp1(x^i;β)(1p1(x^i;β))
通过以上这两种办法我们就解决了,非线性问题中,sigmoid函数激活后的线性函数的变量求解问题。那么既然我们的理论都已经提供了支持,我们接下来就可以使用代码来将点吗一一复现。

2.本次实验数据集介绍

该数据集为经典分类数据集,训练集含有上万张猫和狗的动物的彩色三通道图片(下载链接:dog vs cat | Kaggle )而且这些猫狗都是拥有非常繁多种类,对于训练猫狗分类器模型非常有帮助

在这里插入图片描述

在这里插入图片描述

当然熟悉深度学习的同学肯定会知道,使用卷积神经网络对于彩色图像分类会非常简单。但在这篇博客中我将会介绍怎么在不使用卷积神经网络的情况下,单纯使用logistic一维权重的方法解决这个分类问题。那么如何让系统处理三通道彩色图像呢?下面就让我们开始我们介绍如何处理数据

3.数据处理方式

在本次案例中,为了方便处理三通道彩色图像和符合前面我们说的logistic输入变量形式( x 1 ; x 2 ; x 3 ; . . . x d x_1;x_2;x_3;...x_d x1;x2;x3;...xd),我将图像从多维矩阵直接压缩成一维矩阵,如(64,64,3)压缩成(64 * 64 * 3,)的矩阵:

import cv2
def path_to_data(all_img_path): #循环读取所有图片的路径
    train_set = []
    for path in all_img_path:
        img = cv2.imread(path) #读取图片
        mat = np.array(cv2.resize(img,(64,64))) #将图片大小调整为64*64
        train_set.append(mat.flatten())#使用ndarray自带的flatten方法进行压平
    return train_set #返回存储所有像素数据的矩阵

处理完像素怎么读取,我们接下来自然需要处理标签如何从数据集中读取,那么该数据对于猫狗图像的标签都放在文件名中比如这张狗的图片:

在这里插入图片描述

他的文件名是 dog.12490.jpg,可以看到图像标签藏在图像文件名中我们只需要一点简单的字符串分割方法便可以获得标签。同时我们还需要测试集用于验证我们的模型准确性,这里我们选取1000张图像用作训练集,300张图像用作测试集:

import glob
if __name__ == '__main__':
	train_image_path = glob.glob("dc/train/*.jpg")
    #使用列表推导式获得满足条件的图片
 	train_cat_path = [s for s in train_image_path if s.split('\\')[-1].split('.')[0]=="cat" ]
	train_dog_path = [s for s in train_image_path if s.split('\\')[-1].split('.')[0]=="dog" ] 
    #准备测试集数据,为了分类均匀猫和狗的图像选取一样多的数据量
    test_dog_path = train_cat_path[400:550]
    test_cat_path = train_cat_path[500:650] 
    test_cat_path.extend(test_dog_path)
    test_label = [s.split('\\')[-1].split('.')[0] for s in test_cat_path]
    test_data = path_to_data(test_cat_path)
    label_to_index = {'dog': 0, 'cat': 1} #标签替换字典
    test_labels_nums = [label_to_index.get(l) for l in test_label]#将猫和狗标签使用数字表示
    #####准备训练集数据
    train_cat_path = train_cat_path[:500]
    train_dog_path = train_dog_path[:500]
    train_cat_path.extend(train_dog_path)
    train_image_path = train_cat_path
    random.shuffle(train_image_path) ##训练集打乱数据加强训练模型泛化能力
    train_label = [s.split('\\')[-1].split('.')[0] for s in train_image_path]
    train_labels_nums = [label_to_index.get(l) for l in train_label]
    dataArr = path_to_data(train_image_path)

通过上面的方法,我们就成功读取了数据,将图像像素压平成为一维矩阵,将标签的字符串使用0,1代替(0代表dog,1代表cat。那么完成数据读取之后。我们接下来开始重头戏编写解决logistic回归方法代码。

4.logistic回归方法

随机梯度下降方法

因为我们将数据集进行压缩成为一维矩阵,这个一维矩阵大小其实是非常大的,大的大小足足有(12288,)同时训练数据足足有一千条。我们在单纯只使用CPU的情况下 ,不可能进行整体调整。所以我们这里使用随机梯度下降方法,即我们每次迭代从模型内部随机抽取一条数据放入模型,计算梯度下降的方向,然后使用学习率控制调整的速度(因为数据是随机抽取的一条,无法代表整体调整的方向,所以我们需要控制调整的速度,防止一次调整过多)。 那么让我们回顾以上他的公式:
β t + 1 = β t − α ∂ ℓ ( β ) ∂ β ∂ ℓ ( β ) ∂ β = − ∑ i = 1 m x ^ i ( y i − p 1 ( x ^ i ; β ) ) \boldsymbol{\beta}^{t+1}=\boldsymbol{\beta}^{t}-α\frac{\partial \ell(\boldsymbol{\beta})}{\partial \boldsymbol{\beta}}\\\frac{\partial \ell(\boldsymbol{\beta})}{\partial \boldsymbol{\beta}}=-\sum_{i=1}^{m} \hat{\boldsymbol{x}}_{i}\left(y_{i}-p_{1}\left(\hat{\boldsymbol{x}}_{i} ; \boldsymbol{\beta}\right)\right) βt+1=βtαβ(β)β(β)=i=1mx^i(yip1(x^i;β))
​ 可以发现无论x,y,p都是已知或者是可以求解出来的,所以我们编写代码:

def stocGradAscent1(dataMatrix, classLabels, Epoch=200):
    m,n = np.shape(dataMatrix)
    weights = np.ones(n) #初始化权重全为1
    for i in range(Epoch):
        dataIndex = [x for x in range(m)]
        for j in range(m):
            alpha = 4/(1.0+j+i)+0.01 #防止学习率系数为0,同时是学习率系数也会不断下降
            randInx = int(np.random.uniform(0,len(dataIndex))) #随机抽取值
            h = sigmoid(np.sum(dataMatrix[randInx]*weights))
            error = classLabels[randInx]-h #计算误差
            weights = weights + error*dataMatrix[randInx]*alpha #迭代更新alpha值
            del(dataIndex[randInx])
    return weights#返回权重值

编写完随机梯度下降方法,我们接下来还需要完成数据输入以及在验证集上的数据验证:

import numpy as np
def classify(inX, weights): #控制模型返回数字
    prob = sigmoid(np.sum(inX*weights))
    if prob >0.5:
        return 1.0
    return 0

def dogvsCatTest(trainingSet, trainingLabel, testSet, testLabel):
    trainWeights = stocGradAscent1(np.array(trainingSet), trainingLabel, 500)#训练500次求解梯度
    errorCount = 0
    numTestVec = 0.0
    for i in range(len(testSet)):
        numTestVec +=1.0
        if(int(classify(np.array(testSet[i]),trainWeights))) != testLabel[i]:
            errorCount +=1 #统计错误个数计算错误率
    errorRate = float(errorCount)/numTestVec
    print("错误率是: ", errorRate)
    return errorRate
#随机梯度下降具有随机性运行多次求解模型平均错误率更加准确
for k in range(10):
	errorSum += dogvsCatTest(dataArr,train_labels_nums, test_data, test_labels_nums)
print("%d 次测试,平均错误率为%f" % (10, errorSum / float(10)))

最终运行结果如下:
在这里插入图片描述

可以看到我们的平均错误率只有28.5%左右,模型在三通道彩色图像分类可以算是取得了不错的效果(当然如果你懂得深度学习你就会发现,其实我们本篇编写的模型和以下这些神经网络代码是等价的):

import tensorflow as tf
model = tf.keras.Sequential()
model.add(tf.keras.layers.flatten()) #将数据压平成一维的
model.add(tf.keras.layers.Dense(1, activation='sigmoid'))

其实我们编写的模型和优化方法就相当于是使用单层全连接层神经网络了。

5.结语

在本篇博客中,我详细解释了logistic回归的原理,以及编写随机梯度下降法用于解决该凸函数优化问题。并且在猫狗图像分类数据集完成了模型的训练与验证。取得了72%的优秀准确率(在我们相当于只使用一层全连接层的条件下)。通过对机器学习知识的不断学习,我逐渐了解了他和深度学习的渊源和相关的知识,相信通过对机器学习的不断学习,我对深度学习的理解也将更加深刻,从而更好地对人工智能方面展开研究。对本篇博客有任何意见或者疑问,欢迎在评论区讨论🧐🧐🧐🧐。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值