python机器学习手写算法系列——逻辑回归

2023-11-10

从机器学习到逻辑回归

在这里插入图片描述
今天,我们只关注机器学习到线性回归这条线上的概念。别的以后再说。为了让大家听懂,我这次也不查维基百科了,直接按照自己的理解用大白话说,可能不是很严谨。

机器学习就是机器可以自己学习,而机器学习的方法就是利用现有的数据和算法,解出算法的参数。从而得到可以用的模型。

监督学习就是利用已有的数据(我们叫X,或者特征),和数据的标注(我们叫Y),找到x和y之间的对应关系,或者说是函数f。

回归分析是一种因变量为连续值得监督学习。而分类是一种应变量为非连续值的监督学习。

这里顺便提一句非连续值和连续值的英文有很多表述。

连续值可以是continuous, numerical, quantitative等。

非连续值可以是categorical, nominal, qualitative等。

逻辑回归虽然名字里面有回归两个字,但是它是分类分析,不是回归分析。逻辑回归,它之所以叫这个名字,是因为它和线性回归实在是太像了。

问题

这里,我们使用sklearn自带的癌症数据集。首先读入数据并放入pandas里面。

import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
%matplotlib inline
from sklearn.datasets import load_breast_cancer
#from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

dataset = load_breast_cancer()
data = pd.DataFrame(data=dataset.data, columns=dataset.feature_names)
data['cancer'] = [dataset.target_names[t] for t in dataset.target]

输出两个分类。

print(dataset.target_names)

[‘malignant’ ‘benign’]
翻译成中文是[‘恶性’ ‘良性’]。

输出

data[18:28]
no mean radius mean texture mean perimeter mean area mean smoothness mean compactness mean concavity mean concave points mean symmetry mean fractal dimension worst texture worst perimeter worst area worst smoothness worst compactness worst concavity worst concave points worst symmetry worst fractal dimension cancer
18 19.810 22.15 130.00 1260.0 0.09831 0.10270 0.14790 0.09498 0.1582 0.05395 30.88 186.80 2398.0 0.1512 0.3150 0.53720 0.23880 0.2768 0.07615 malignant
19 13.540 14.36 87.46 566.3 0.09779 0.08129 0.06664 0.04781 0.1885 0.05766 19.26 99.70 711.2 0.1440 0.1773 0.23900 0.12880 0.2977 0.07259 benign
20 13.080 15.71 85.63 520.0 0.10750 0.12700 0.04568 0.03110 0.1967 0.06811 20.49 96.09 630.5 0.1312 0.2776 0.18900 0.07283 0.3184 0.08183 benign
21 9.504 12.44 60.34 273.9 0.10240 0.06492 0.02956 0.02076 0.1815 0.06905 15.66 65.13 314.9 0.1324 0.1148 0.08867 0.06227 0.2450 0.07773 benign
22 15.340 14.26 102.50 704.4 0.10730 0.21350 0.20770 0.09756 0.2521 0.07032 19.08 125.10 980.9 0.1390 0.5954 0.63050 0.23930 0.4667 0.09946 malignant
23 21.160 23.04 137.20 1404.0 0.09428 0.10220 0.10970 0.08632 0.1769 0.05278 35.59 188.00 2615.0 0.1401 0.2600 0.31550 0.20090 0.2822 0.07526 malignant
24 16.650 21.38 110.00 904.6 0.11210 0.14570 0.15250 0.09170 0.1995 0.06330 31.56 177.00 2215.0 0.1805 0.3578 0.46950 0.20950 0.3613 0.09564 malignant
25 17.140 16.40 116.00 912.7 0.11860 0.22760 0.22290 0.14010 0.3040 0.07413 21.40 152.40 1461.0 0.1545 0.3949 0.38530 0.25500 0.4066 0.10590 malignant
26 14.580 21.53 97.41 644.8 0.10540 0.18680 0.14250 0.08783 0.2252 0.06924 33.21 122.40 896.9 0.1525 0.6643 0.55390 0.27010 0.4264 0.12750 malignant
27 18.610 20.25 122.10 1094.0 0.09440 0.10660 0.14900 0.07731 0.1697 0.05699 27.26 139.90 1403.0 0.1338 0.2117 0.34460 0.14900 0.2341 0.07421 malignant

这里一共有30个属性。

手写算法

在这里插入图片描述

无论是线性回归,逻辑回归,以及我以后会写文章的神经网络,他们的基本思路都是一样的。首先,构建一个函数模型,用这个函数表示从x到y的映射关系。

然后构建一个损失函数loss function。它描述了模型函数 f ( x ) f(x) f(x)和真实值 y y y之间的差距。当然,这个差距越小越好。

最后是优化方法。优化方法首先计算损失函数对参数的导数。既,损失函数随参数是变大还是变小的。如果损失函数随着参数的变大而变小,则说明参数应该变大。如果损失函数随着参数的变小而变小,则说明参数应该变小。优化方法里面的 α \alpha α是学习率,一个小于1的数值,可以是0.01,0.001, 甚至更小。

模型函数

这里,我们的y值,并非连续的。要么 y = 0 y=0 y=0,要么 y = 1 y=1 y=1。所以,和线性回归相比,我们要把 y y y控制在0和1之间。这时,前人引进了sigmoid函数。当x大于0时,y无限接近于1;当x小于0时,y无限接近于0;当x等于0时,y=0.5。

y ^ = 1 1 + e − z \hat{y}=\frac{1}{1 + e^{- z}} y^=1+ez1 其中 z = θ x z=\theta x z=θx

def sigmoid(z):
    s = 1/(1+np.exp(-z))
    s = s.reshape(s.shape[0],1)
    return s

我们可以把这个函数画出来看看。

def draw_sigmoid():
    x = np.arange(-6, 6, .01)
    y = sigmoid(x)

    plt.plot(x, y, color='red', lw=2)
    plt.show()

draw_sigmoid()

在这里插入图片描述
最终,我们的模型函数是:

def model(theta, X):
    z = np.sum(theta.T * X, axis=1)
    return sigmoid(z)

损失函数

这里,损失函数用的是cross entropy,的定义如下。

J = − y ∗ l o g ( y ^ ) − ( 1 − y ) ∗ l o g ( 1 − y ^ ) J= - y * log(\hat{y}) - (1-y) * log(1-\hat{y}) J=ylog(y^)(1y)log(1y^)

#cross_entropy
def cross_entropy(y, y_hat):
    n_samples = y.shape[0]
    return sum(-y*np.log(y_hat)-(1-y)*np.log(1-y_hat))/n_samples

def cost_function(theta, X, y):
    y_hat = model(theta, X)
    return cross_entropy(y, y_hat)

优化函数

这里先要解决 ∂ J ∂ θ \frac{\partial J}{\partial \theta} θJ

因为有

J = − y ∗ l o g ( y ^ ) − ( 1 − y ) ∗ l o g ( 1 − y ^ ) J= - y * log(\hat{y}) - (1-y) * log(1-\hat{y}) J=ylog(y^)(1y)log(1y^)

所以

∂ J ∂ θ = − ∂ ( y ∗ l o g ( y ^ ) + ( 1 − y ) ∗ l o g ( 1 − y ^ ) ) ∂ θ \frac{\partial J}{\partial \theta} = - \frac{\partial (y * log(\hat{y}) + (1-y) * log(1-\hat{y}))}{\partial \theta} θJ=θ(ylog(y^)+(1y)log(1y^))

= − ∂ ( y ∗ l o g ( y ^ ) ) ∂ θ − ∂ ( ( 1 − y ) ∗ l o g ( 1 − y ^ ) ) ∂ θ = - \frac{\partial (y * log(\hat{y}))}{\partial \theta} - \frac{\partial ((1-y) * log(1-\hat{y}))}{\partial \theta} =θ(ylog(y^))θ((1y)log(1y^))

= − y ∗ ∂ l o g ( y ^ ) ∂ θ − ( 1 − y ) ∗ l o g ( 1 − y ^ ) ∂ θ = - y * \frac{\partial log(\hat{y})}{\partial \theta} - (1-y) * \frac{log(1-\hat{y})}{\partial \theta} =yθlog(y^)(1y)θlog(1y^)

= − y y ^ ∗ ∂ y ^ ∂ θ − 1 − y 1 − y ^ ∗ ∂ ( 1 − y ^ ) ∂ θ = - \frac{y}{\hat{y}} *\frac{\partial \hat{y}}{\partial \theta} - \frac{1-y}{1-\hat{y}} *\frac{\partial (1-\hat{y})}{\partial \theta} =y^yθy^1y^1yθ(1y^)

= − y y ^ ∗ ∂ y ^ ∂ θ + 1 − y 1 − y ^ ∗ ∂ y ^ ∂ θ = - \frac{y}{\hat{y}} *\frac{\partial \hat{y}}{\partial \theta} + \frac{1-y}{1-\hat{y}} *\frac{\partial \hat{y}}{\partial \theta} =y^yθy^+1y^1yθy^

= ( − y y ^ + 1 − y 1 − y ^ ) ∗ ∂ y ^ ∂ θ = (- \frac{y}{\hat{y}} + \frac{1-y}{1-\hat{y}} ) * \frac{\partial \hat{y}}{\partial \theta} =(y^y+1y^1y)θy^

= ( − y y ^ + 1 − y 1 − y ^ ) ∗ ∂ ( 1 1 + e − θ x ) ∂ a = (- \frac{y}{\hat{y}} + \frac{1-y}{1-\hat{y}} ) * \frac{\partial (\frac{1}{1 + e^{- \theta x}})} {\partial a} =(y^y+1y^1y)a(1+eθx1)

= ( − y y ^ + 1 − y 1 − y ^ ) ∗ ( − y ^ 2 ) ∗ ∂ ( 1 + e − θ x ) ∂ θ = (- \frac{y}{\hat{y}} + \frac{1-y}{1-\hat{y}} ) * (- \hat{y} ^ 2) * \frac{\partial (1 + e^{- \theta x})} {\partial \theta} =(y^y+1y^1y)(y^2)θ(1+eθx)

= ( − y y ^ + 1 − y 1 − y ^ ) ∗ ( − y ^ 2 ) ∗ ∂ ( e − θ x ) ∂ θ = (- \frac{y}{\hat{y}} + \frac{1-y}{1-\hat{y}} ) * (- \hat{y} ^ 2) * \frac{\partial (e^{- \theta x})} {\partial \theta} =(y^y+1y^1y)(y^2)θ(eθx)

= ( − y y ^ + 1 − y 1 − y ^ ) ∗ ( − y ^ 2 ) ∗ e − θ x ∗ ∂ ( − θ x ) ∂ θ = (- \frac{y}{\hat{y}} + \frac{1-y}{1-\hat{y}} ) * (- \hat{y} ^ 2) * e^{- \theta x} * \frac{\partial (- \theta x)} {\partial \theta} =(y^y+1y^1y)(y^2)eθxθ(θx)

= ( − y y ^ + 1 − y 1 − y ^ ) ∗ y ^ 2 ∗ e − θ x ∗ x = (- \frac{y}{\hat{y}} + \frac{1-y}{1-\hat{y}} ) * \hat{y} ^ 2 * e^{- \theta x} * x =(y^y+1y^1y)y^2eθxx

y ^ = 1 1 + e − θ x \hat{y}=\frac{1}{1 + e^{- \theta x}} y^=1+eθx1

1 + e − θ x = 1 y ^ 1 + e^{- \theta x}=\frac{1}{\hat{y}} 1+eθx=y^1

e − θ x = 1 y ^ − 1 e^{- \theta x}=\frac{1}{\hat{y}} - 1 eθx=y^11

e − θ x = 1 − y ^ y ^ e^{- \theta x}=\frac{1-\hat{y}}{\hat{y}} eθx=y^1y^

∂ J ∂ θ = ( − y y ^ + 1 − y 1 − y ^ ) ∗ y ^ 2 ∗ 1 − y ^ y ^ ∗ x \frac{\partial J}{\partial \theta} = (- \frac{y}{\hat{y}} + \frac{1-y}{1-\hat{y}} ) * \hat{y} ^ 2 * \frac{1-\hat{y}}{\hat{y}} * x θJ=(y^y+1y^1y)y^2y^1y^x

∂ J ∂ θ = ( − y y ^ + 1 − y 1 − y ^ ) ∗ y ^ ∗ ( 1 − y ^ ) ∗ x \frac{\partial J}{\partial \theta} = (- \frac{y}{\hat{y}} + \frac{1-y}{1-\hat{y}} ) * \hat{y} * (1-\hat{y}) * x θJ=(y^y+1y^1y)y^(1y^)x

∂ J ∂ θ = ( − y ∗ ( 1 − y ^ ) + ( 1 − y ) ∗ y ^ ) ∗ x \frac{\partial J}{\partial \theta} = (- y * (1-\hat{y}) + (1-y) * \hat{y} ) * x θJ=(y(1y^)+(1y)y^)x

∂ J ∂ θ = ( − y + y ∗ y ^ + y ^ − y ∗ y ^ ) ∗ x \frac{\partial J}{\partial \theta} = (- y + y *\hat{y} + \hat{y}-y * \hat{y} ) * x θJ=(y+yy^+y^yy^)x

∂ J ∂ θ = ( y ^ − y ) ∗ x \frac{\partial J}{\partial \theta} = (\hat{y}-y ) * x θJ=(y^y)x

最后,得到优化函数:

θ = θ − α ∗ ∂ J ∂ θ \theta = \theta - \alpha * \frac{\partial J}{\partial \theta} θ=θαθJ

θ = θ − α ∗ ( y ^ − y ) ∗ x \theta = \theta - \alpha * (\hat{y}-y ) * x θ=θα(y^y)x

python函数如下:

def optimize(theta,X,y):
    n = X.shape[0]
    alpha = 1e-1
    y_hat = model(theta,X)
    dtheta = (1.0/n) * ((y_hat-y)*X)
    dtheta = np.sum(dtheta, axis=0)
    dtheta=dtheta.reshape((31,1))
    theta = theta - alpha * dtheta
    return theta

评估

分类问题的评估比较简单,一般用准确率就可以了。当然也可以用别的(召回率,Precision,ROC,F1 Score等等),其公式如下。

def predict_proba(theta, X):
    y_hat=model(theta, X)
    return y_hat

def predict(X, theta):
    y_hat=predict_proba(theta,X)
    y_hard=(y_hat > 0.5) * 1
    return y_hard

def accuracy(theta, X, y):
    y_hard=predict(X, theta)
    count_right=sum(y_hard == y)
    return count_right*1.0/len(y)

循环函数

我们不断的调用优化函数来更新参数。

def iterate(theta,X,y,times):
    costs = []
    accs = []
    for i in range(times):
        theta = optimize(theta,X,y)
        costs.append(cost_function(theta, X, y))
        accs.append(accuracy(theta, X, y))

    return theta, costs, accs

使用算法

准备数据

加载数据

X = dataset.data
y = dataset.target
n_features = X.shape[1]

归一化

std=X.std(axis=0)
mean=X.mean(axis=0)
X_norm = (X - mean) / std

在x矩阵前面加上一列1,这样做的好处是,不需要单独处理截距(interception)。前面的优化方法的推导,已经很麻烦了。如果把截距和斜率(coefficient)分开处理,我又要推导一遍。并且这样程序变得很复杂,很难维护了。

def add_ones(X):
    ones=np.ones((X.shape[0],1))
    X_with_ones=np.hstack((ones, X))
    return X_with_ones
X_with_ones = add_ones(X_norm)

求参数

首先,初始化参数。我这里都为1。

theta = np.ones((n_features+1,1))

接着,就可以一直循环求参数了。

theta, costs, accs = iterate(theta, X_train, y_train, 1500)

随着不断的训练,loss不断下降。
在这里插入图片描述
(放大到1到100)
在这里插入图片描述

而准确率不断提升。

在这里插入图片描述

(放大到1到100)

在这里插入图片描述
我们最终的loss和准确率是

print(costs[-1], accs[-1])

[0.0489982] [0.99246231]

用测试数据评估为

accuracy(theta, X_test, y_test)

array([0.97660819])

用sklearn计算

from sklearn.linear_model import LogisticRegression
X = dataset.data
y = dataset.target
X_train, X_test, y_train, y_test = train_test_split(X_with_ones, y, test_size = 0.3, random_state=12345)
lr = LogisticRegression()
lr.fit(X_train, y_train)

训练数据集准确率

lr.score(X_train, y_train)

0.992462311557789

测试数据集准确率

lr.score(X_test, y_test)

0.9766081871345029

可以看到,这里的准确率和我们手写的逻辑回归是一样的。大功告成了!

模型参数解释

把函数

y ^ = 1 1 + e − θ x \hat{y}=\frac{1}{1 + e^{- \theta x}} y^=1+eθx1

分解得到

y ^ = 1 1 + e − θ 0 − θ 1 x 1 − θ 2 x 2 − θ 3 x 3 \hat{y}=\frac{1}{1 + e^{- \theta_0 - \theta_1 x_1 - \theta_2 x_2 - \theta_3 x_3}} y^=1+eθ0θ1x1θ2x2θ3x31

y ^ = 1 1 + e − θ 0 e − θ 1 x 1 e − θ 2 x 2 e − θ 3 x 3 \hat{y}=\frac{1}{1 + e^{- \theta_0} e^{- \theta_1 x_1} e^{ - \theta_2 x_2 } e^{- \theta_3 x_3}} y^=1+eθ0eθ1x1eθ2x2eθ3x31

θ 1 \theta_1 θ1为0时, e − θ 1 x 1 = 1 e^{- \theta_1 x_1}=1 eθ1x1=1 x 1 x_1 x1的变化对结果没有影响。

θ 1 \theta_1 θ1为大于0时, e − θ 1 x 1 e^{- \theta_1 x_1} eθ1x1 x 1 x_1 x1的增大而变小 ,而分母变小则 y ^ \hat{y} y^变大。既, y ^ \hat{y} y^随着 x 1 x_1 x1的变大而变大。

反之,当 θ 1 \theta_1 θ1为小于0时, y ^ \hat{y} y^随着 x 1 x_1 x1的变大而变小。

我们这里以mean radius为例,它的参数为-0.356072,所以, y ^ \hat{y} y^随着mean radius的变大而变小。而0对应的是恶性,1对应的是良性。我们可以说,mean radius越大,越有可能是恶行肿瘤。

python机器学习手写算法系列

完整源代码:

https://github.com/juwikuang/machine_learning_step_by_step

欢迎阅读本系列其他文章:

《python机器学习手写算法系列——线性回归》

《python机器学习手写算法系列——逻辑回归》

《python机器学习手写算法系列——决策树》

《python机器学习手写算法系列——kmeans聚类》

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

python机器学习手写算法系列——逻辑回归 的相关文章

  • 操作数无法与形状 (128,) (0,) 错误一起广播

    我正在尝试实现面部识别登录系统 但出现错误 操作数无法与形状 128 0 一起广播 我不知道什么或如何解决它 这是我已实现的 view py 和 FaceDetector py 以及我从服务器收到的错误 errors Traceback m
  • python导入模块时如何避免一直写模块名?

    我用math最近模块很多 我不想写math sqrt x and math sin x 每时每刻 我想缩短它并写sqrt x and sin x How 对于较长的模块名称 通常会缩短它们 例如 import numpy as np 然后您
  • Python Nose 导入错误

    我似乎无法理解鼻子测试框架 https nose readthedocs org en latest 识别文件结构中测试脚本下方的模块 我已经设置了演示该问题的最简单的示例 下面我会解释一下 这是包文件结构 init py foo py t
  • DataFrame 在函数内部修改

    我面临一个我以前从未观察到的函数内数据帧修改的问题 有没有一种方法可以处理这个问题 以便初始数据帧不被修改 def test df df tt np nan return df dff pd DataFrame data 现在 当我打印时d
  • Spark MLlib - 训练隐式警告

    我在使用时不断看到这些警告trainImplicit WARN TaskSetManager Stage 246 contains a task of very large size 208 KB The maximum recommend
  • KFold 和 ShuffleSplit CV 有什么区别?

    看起来 KFold 每次迭代对象时都会生成相同的值 而 Shuffle Split 每次都会生成不同的索引 它是否正确 如果是这样 其中一个相对于另一个有什么用处 cv cross validation KFold 10 n folds 2
  • 将 API 数据存储到 DataFrame 中

    我正在运行 Python 脚本来从 Interactive Brokers API 收集金融市场数据 连接到API后 终端打印出请求的历史数据 如何将数据保存到数据帧中而不是在终端中流式传输 from ibapi wrapper impor
  • 如何从谷歌云存储桶读取音频文件并在datalab笔记本中使用ipd播放

    我想在数据实验室笔记本中播放我从谷歌云存储桶中读取的声音文件 这个怎么做 import numpy as np import IPython display as ipd import librosa import soundfile as
  • 字典中的列表,Python 中的循环

    我有以下代码 TYPES hotmail type hotmail lookup mixed dkim no signatures S Return Path email protected cdn cgi l email protecti
  • 如何找到列表S的所有分区为k个子集(可以为空)?

    我有一个唯一元素列表 比方说 1 2 我想将其拆分为 k 2 个子列表 现在我想要所有可能的子列表 1 2 1 2 2 1 1 2 我想分成 1 1 2 我怎样才能用 Python 3 做到这一点 更新 我的目标是获取 N 个唯一数字列表的
  • 为什么我的scoped_session 引发 AttributeError: 'Session' object has no attribute 'remove'

    我正在尝试建立一个系统 将数据库操作优雅地推迟到单独的线程 以避免在 Twisted 回调期间发生阻塞 到目前为止 这是我的方法 from contextlib import contextmanager from sqlalchemy i
  • 在Python中创建一个新表

    我正在尝试从数控机床中提取数据 事件每毫秒发生一次 我需要过滤掉一些用管道 分隔的变量分隔符 PuTTy exe 程序生成的日志文件 我尝试阅读熊猫 但列不在同一位置 df pd read table data log sep 日志文件的一
  • smooth_idf 是多余的吗?

    The scikit learn 文档 http scikit learn org stable modules generated sklearn feature extraction text TfidfTransformer html
  • numpy.cov() 返回意外的输出

    我有一个 X 数据集 有 9 个特征和 683 行 683x9 我想获取这个 X 数据集和另一个与 X 具有相同形状的数据集的协方差矩阵 我使用np cov originalData generatedData rowvar False 代
  • 如何在C++中列出Python模块的所有函数名称?

    我有一个 C 程序 我想导入一个 Python 模块并列出该模块中的所有函数名称 我该怎么做 我使用以下代码从模块中获取字典 PyDictObject pDict PyDictObject PyModule GetDict pModule
  • 大型数据集上的 Sklearn-GMM

    我有一个很大的数据集 我无法将整个数据放入内存中 我想在这个数据集上拟合 GMM 我可以用吗GMM fit sklearn mixture GMM 重复小批量数据 没有理由重复贴合 只需随机采样您认为机器可以在合理时间内计算的尽可能多的数据
  • 为什么 bot.get_channel() 会产生 NoneType?

    我正在制作一个 Discord 机器人来处理公告命令 当使用该命令时 我希望机器人在特定通道中发送一条消息 并向用户发送一条消息以表明该命令已发送 但是 我无法将消息发送到频道 我尝试了这段代码 import discord import
  • 最小硬币找零问题——回溯

    我正在尝试用最少数量的硬币解决硬币找零问题 采用回溯法 我实际上已经完成了它 但我想添加一些选项 按其单位打印硬币数量 而不仅仅是总数 这是我下面的Python代码 def minimum coins coin list change mi
  • SQLAlchemy:避免声明式样式类定义中的重复

    我正在使用 SQLAlchemy 并且我的对象模型中的许多类具有相同的两个属性 id 和 整数和主键 以及名称 字符串 我试图避免在每个类中声明它们 如下所示 class C1 declarative base id Column Inte
  • 如何使用Featuretools按列值从单个数据框中的多个列创建特征?

    我正在尝试根据之前的结果来预测足球比赛的结果 我在 Windows 上运行 Python 3 6 并使用 Featuretools 0 4 1 假设我有以下代表结果历史记录的数据框 原始数据框 https i stack imgur com

随机推荐