ML03-逻辑回归(下部分)
本文主题-逻辑回归(下部分):
- 逻辑回归的应用背景
- 逻辑回归的数学基础
- 逻辑回归的模型与推导
- 逻辑回归算法推导
- 梯度下降算法
- 逻辑回归梯度下降算法实现
七、线性回归sklearn实现
- 采用极端样本分类
from sklearn.linear_model import *
import numpy as np
cls1_data = np.random.uniform(low=1,high=10,size=50)
cls1_result = np.ones(shape=(50),dtype=np.int32)
cls2_data = np.random.uniform(low=0,high=3,size=50)
cls2_result = np.zeros(shape=(50),dtype=np.int32)
x=np.hstack((cls1_data,cls2_data))
y=np.hstack((cls1_result,cls2_result))
x=x.reshape(-1, 1)
# 逻辑回归
logreg = LogisticRegression(C=10e10,max_iter=10000)
logreg.fit(x, y)
print('训练样本预测结果:',logreg.predict(x))
print('识别精确率:',logreg.score(x,y)) #
训练样本预测结果: [1 0 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 0 1 1
0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0]
识别精确率: 0.92
- 可视化训练结果
import matplotlib.pyplot as plt
figure = plt.figure('机器学习sklearn可视化',figsize=(6,4))
ax = figure.add_axes([0.1, 0.1, 0.8, 0.8], label='逻辑回归')
ax.set_xlim(left=0,right=12)
ax.set_ylim(bottom=0,top=1.5)
ax.set_xlabel('X')
ax.set_ylabel('Y')
# 绘制训练样本
ax.scatter(x=x, y=y,color='b',label='训练样本')
X=np.linspace(0,10,100)
Y=logreg.predict(X.reshape(-1,1))
print(Y)
# 绘制直线
ax.plot(X,Y,color='r',label='逻辑回归:选择输出')
s=lambda p:1.0/(1+np.exp(-p))
ax.plot(X,s(X.reshape(100,1)@logreg.coef_+logreg.intercept_),color='g',label='逻辑回归:实际输出')
ax.plot(X,[0.5 for i in x],color=(1,1,0,1),label='概率0.5')
ax.legend()
plt.show()
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]
极端样本分类可视化
- 采用身高体重样本
逻辑回归只适用于0-1输出的分类,不适合连续值。
下面代码会报错误。
from sklearn.linear_model import *
import numpy as np
X_DATA = np.loadtxt('ex2x.dat')
# 身高
Y_DATA = np.loadtxt('ex2y.dat')
X_DATA=X_DATA.reshape(-1, 1)
# 逻辑回归
logreg = LogisticRegression(C=10e10,max_iter=10000)
logreg.fit(X_DATA, Y_DATA)
print('训练样本预测结果:',logreg.predict(X_DATA))
print('识别精确率:',logreg.score(X_DATA,Y_DATA)) #
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-206-f21a7dbee995> in <module>()
11 # 逻辑回归
12 logreg = LogisticRegression(C=10e10,max_iter=10000)
---> 13 logreg.fit(X_DATA, Y_DATA)
14
15 print('训练样本预测结果:',logreg.predict(X_DATA))
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sklearn/linear_model/logistic.py in fit(self, X, y, sample_weight)
1219 X, y = check_X_y(X, y, accept_sparse='csr', dtype=_dtype,
1220 order="C")
-> 1221 check_classification_targets(y)
1222 self.classes_ = np.unique(y)
1223 n_samples, n_features = X.shape
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sklearn/utils/multiclass.py in check_classification_targets(y)
169 if y_type not in ['binary', 'multiclass', 'multiclass-multioutput',
170 'multilabel-indicator', 'multilabel-sequences']:
--> 171 raise ValueError("Unknown label type: %r" % y_type)
172
173
ValueError: Unknown label type: 'continuous'
- 采用鸢尾花样本(第一类与第二类者第三类)
该样本的50个与后面100个是线性可分的,第50-100与100-150是线性不可分
from sklearn.linear_model import *
import numpy as np
from sklearn import datasets
data,target=datasets.load_iris(return_X_y=True)
X_DATA=data[0:100]
Y_DATA=target[0:100]
# 逻辑回归
logreg = LogisticRegression(C=10e10,max_iter=10000)
logreg.fit(X_DATA, Y_DATA)
print('训练样本预测结果:',logreg.predict(X_DATA))#
print('识别精确率:%5.2f%%'%(logreg.score(X_DATA,Y_DATA)*100)) #
训练样本预测结果: [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]
识别精确率:100.00%
- 采用鸢尾花样本(第二类与第三类)
该样本的50个与后面100个是线性可分的,第50-100与100-150是线性不可分
from sklearn.linear_model import *
import numpy as np
from sklearn import datasets
data,target=datasets.load_iris(return_X_y=True)
X_DATA=data[50:150]
Y_DATA=target[0:100]
# 逻辑回归
logreg = LogisticRegression(C=10e10,max_iter=10000)
logreg.fit(X_DATA, Y_DATA)
print(logreg.coef_)
print(logreg.intercept_)
print('训练样本预测结果:',logreg.predict(X_DATA))#
print('识别精确率:%5.2f%%'%(logreg.score(X_DATA,Y_DATA)*100)) #
[[-2.46368253 -6.67654959 9.41722578 18.27097595]]
[-42.57423423]
训练样本预测结果: [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]
识别精确率:98.00%
八、tensorflow实现
- 梯度下降法几个条件
|-决策计算模型
|-
|-损失函数(版本一)
|-
|-损失函数(版本二)有的为了计算的方便会增加一个因子,看起来像求均值。
|-
-
梯度下降通用推导
|-梯度计算(通用版)
|-
|-根据链式偏导可以分解求导参数的链式分解:
|-
|-
|-
|-这种通用的梯度推导结构由三个因素决定:
|-(1)损失函数的导数
|-(2)输出控制函数的导数
|-(3)输入特征
|-通常把前面两项称为误差项:
|-
|-可以使用导数表示误差项:
|-
注意:这里我们使用了一个符号用来区分,表示理想输出值,表示样本实际输出结果。
|- 其中用来控制梯度下降速度
梯度更新公式:
|- -
梯度计算的验证
由于梯度核心计算就是误差项的计算,该值容易计算错误,如果大家理解误差项的计算方式,很容易找到另外一种验证方式(也可以做为导数的近似计算方式 ),就是损失函数的导数,实际是另外一种极限的表示:
|-
|- 当然必须任意小 -
线性回归中的梯度下降关键公式表示
|-损失函数: 某些时候可以加一个因子
|-决策模型:
|-线性模型: -
逻辑回归中的梯度下降关键公式表示
|-损失函数: 某些时候可以加一个因子
|-决策模型: S函数一个比较好的性质就是其导数可以用自己表示
|-线性模型:
我们称在线性模型之上的运算函数为激活函数(这是神经网络的用语) -
关于线性回归与逻辑回归的共性
从上面的梯度下降法中可以看出,线性回归与逻辑回归只是在损失函数与决策模型输出有差异,但还是遵循一个线性计算。 所以都是广义上的线性回归模型。(这种共性在神经网络中被统一起来,而且还有很多其他的决策模型与损失函数,再神经网络中,决策模型称为激活函数,激活函数决定着输出特征在下一层特征融合中的重要性 ) -
Tensorflow实现结构
(1)编程面向过程的结构
import numpy as np
import tensorflow as tf
# 我们使用鸢尾花数据作为分类测试
# 1. 定义数据(一般是样本与样本标签)
# 2. 决策参数模型描述
# 3. 梯度下降参数模型
# 4. tensorflow运算执行会话环境
# 5. 执行与训练
# 6. 预测与分类评估
(2)执行环境
import numpy as np
import tensorflow as tf
'''
tf.initializers.global_variables()
初始化Tensorflow全局变量
两种方式:
|-tf.global_variables_initializer()
|-tf.initializers.global_variables()
全局变量返回:
|-tf.global_variables()
Session构造器:
__init__(
target='',
graph=None,
config=None
)
Session.run函数
run(
fetches,
feed_dict=None,
options=None,
run_metadata=None
)
'''
# 我们使用鸢尾花数据作为分类测试
# 1. 定义数据(一般是样本与样本标签)
# 2. 决策参数模型描述
# 3. 梯度下降参数模型
# 4. tensorflow运算执行会话环境
op_init=tf.initializers.global_variables()
# print(op_init)
# print(tf.initializers.global_variables())
session=tf.Session()
# 没有全局变量,会返回None.
session.run(op_init)
# print(re)
# 5. 执行与训练
# 6. 预测与分类评估
None
(3)定义输入输出量
import numpy as np
import tensorflow as tf
from sklearn import datasets
'''
占位张量
tf.placeholder(
dtype,
shape=None,
name=None
)
在张量shape的表示也需要注意,标量形状使用[ ]表示。
标量形状表示 [ ]:比如3
向量形状表示 [3]:比如[1., 2., 3.]
矩阵形状表述[2, 3]:表示行2列3的矩阵、
高阶张量形状表示[2,4,8]。
如果张量某个维度大小未知,在输入数据确定,可以使用None。
'''
# 我们使用鸢尾花数据作为分类测试
INPUT_SIZE=4
OUTPUT_SIZE=1
# 1. 定义数据(一般是样本与样本标签)
# 1.1. 输入数据(站位方式)
x=tf.placeholder(dtype=tf.float64,shape=[None,INPUT_SIZE])
y=tf.placeholder(dtype=tf.float64,shape=[None,OUTPUT_SIZE])
print(x)
print(y)
# 1.2. 权重数据 (考虑偏置项,可以认为是x=1的权重项)注意权重形状大小
# 2. 决策参数模型描述
# 3. 梯度下降参数模型
# 4. tensorflow运算执行会话环境
op_init=tf.initializers.global_variables()
session=tf.Session()
session.run(op_init)
# 5. 执行与训练
# 6. 预测与分类评估
(4)执行操作
import numpy as np
import tensorflow as tf
from sklearn import datasets
'''
run(
fetches, # 需要计算的表达式
feed_dict=None, # 输入与输出,数据采用字典格式
options=None,
run_metadata=None
)
feed_dict={
占位变量: 实际值,
......
}
返回fetches对应的数据。
'''
INPUT_SIZE=4
OUTPUT_SIZE=1
# 我们使用鸢尾花数据作为分类测试
# 1. 定义数据(一般是样本与样本标签)
# 1.1. 输入数据(站位方式)
x=tf.placeholder(dtype=tf.float64,shape=[None,INPUT_SIZE])
y=tf.placeholder(dtype=tf.float64,shape=[None,OUTPUT_SIZE])
# 1.2. 权重数据 (考虑偏置项,可以认为是x=1的权重项)注意权重形状大小
# 2. 决策参数模型描述
# 3. 梯度下降参数模型
# 4. tensorflow运算执行会话环境
op_init=tf.initializers.global_variables()
session=tf.Session()
session.run(op_init)
# 5. 执行与训练
# 加载输入(样本特征)与输出数据(实际分类的标签)
data,target=datasets.load_iris(return_X_y=True)
# 对target坐下形状格式处理
target=target.reshape(target.shape[0],OUTPUT_SIZE)
X,Y=session.run( [x,y],feed_dict={x:data,y:target})
# print(X,Y)
# 6. 预测与分类评估
(5)定义权重-训练线性系数
import numpy as np
import tensorflow as tf
from sklearn import datasets
'''
__init__(
initial_value=None,
trainable=True,
collections=None,
validate_shape=True,
caching_device=None,
name=None,
variable_def=None,
dtype=None,
expected_shape=None,
import_scope=None,
constraint=None,
use_resource=None,
synchronization=tf.VariableSynchronization.AUTO,
aggregation=tf.VariableAggregation.NONE
)
其中有意义的是:
|-initial_value
|-dtype
有的张量变量可以使用函数产生,比如随机采样张量数据:
|-tf.random_normal 正态分布
|-tf.random_uniform 均匀分布
|-tf.random_npoisson 泊松分布
|-tf.random_gamma 伽马分布
|-tf.range 连续序列
|-tf.zeros
很多函数与类有自己的包路径,同时为了方便,也在全局中定义别名。比如随机取样:
|-tf.random.uniform()
|-tf.random_uniform()
随机取样函数:
tf.random.uniform(
shape,
minval=0,
maxval=None,
dtype=tf.float32,
seed=None,
name=None
)
'''
INPUT_SIZE=4
OUTPUT_SIZE=1
# 我们使用鸢尾花数据作为分类测试
# 1. 定义数据(一般是样本与样本标签)
# 1.1. 输入数据(站位方式)
x=tf.placeholder(dtype=tf.float64,shape=[None,INPUT_SIZE])
y=tf.placeholder(dtype=tf.float64,shape=[None,OUTPUT_SIZE])
# 1.2. 权重数据 (考虑偏置项,可以认为是x=1的权重项)注意权重形状大小
w=tf.random_uniform(shape=(INPUT_SIZE,OUTPUT_SIZE),minval=-0.01,maxval=0.01,dtype=tf.float32)
b=tf.random_uniform(shape=[OUTPUT_SIZE],minval=-0.01,maxval=0.01,dtype=tf.float32)
print(w)
print(b)
# 2. 决策参数模型描述
# 3. 梯度下降参数模型
# 4. tensorflow运算执行会话环境
op_init=tf.initializers.global_variables()
session=tf.Session()
session.run(op_init)
# 5. 执行与训练
# 加载输入(样本特征)与输出数据(实际分类的标签)
data,target=datasets.load_iris(return_X_y=True)
# 对target坐下形状格式处理
target=target.reshape(target.shape[0],OUTPUT_SIZE)
# 6. 预测与分类评估
Tensor("random_uniform:0", shape=(4, 1), dtype=float32)
Tensor("random_uniform_1:0", shape=(1,), dtype=float32)
(6)定义决策模型
import numpy as np
import tensorflow as tf
import tensorflow.math as ma
from sklearn import datasets
'''
tf.math.sigmoid(
x,
name=None
)
还有别名调用方式:
|-tf.math.sigmoid
|-tf.nn.sigmoid
|-tf.sigmoid
'''
INPUT_SIZE=4
OUTPUT_SIZE=1
# 我们使用鸢尾花数据作为分类测试
# 1. 定义数据(一般是样本与样本标签)
x=tf.placeholder(dtype=tf.float32,shape=[None,INPUT_SIZE])
y=tf.placeholder(dtype=tf.float32,shape=[None,OUTPUT_SIZE])
w=tf.random_uniform(shape=(INPUT_SIZE,OUTPUT_SIZE),minval=-0.01,maxval=0.01,dtype=tf.float32)
b=tf.random_uniform(shape=[OUTPUT_SIZE],minval=-0.01,maxval=0.01,dtype=tf.float32)
# 2. 决策参数模型描述
o_y=tf.matmul(x,w)+b
o_predict=tf.sigmoid(o_y)
# 3. 梯度下降参数模型
# 4. tensorflow运算执行会话环境
op_init=tf.initializers.global_variables()
session=tf.Session()
session.run(op_init)
# 5. 执行与训练
# 加载输入(样本特征)与输出数据(实际分类的标签)
data,target=datasets.load_iris(return_X_y=True)
# 对target坐下形状格式处理
target=target.reshape(target.shape[0],OUTPUT_SIZE)
o_v=session.run(o_predict, feed_dict={x:data})
# 6. 预测与分类评估
(7)定义损失函数
import numpy as np
import tensorflow as tf
import tensorflow.math as ma
import tensorflow.losses as ls
from sklearn import datasets
'''
损失函数在tensorflow中有定义
tf.losses.sigmoid_cross_entropy(
multi_class_labels,
logits,
weights=1.0,
label_smoothing=0,
scope=None,
loss_collection=tf.GraphKeys.LOSSES,
reduction=Reduction.SUM_BY_NONZERO_WEIGHTS
)
其中比较难以理解的参数说明:
|-multi_class_labels : 等价于真实标签,
|-logits : 等价理想决策输出
另外几个参数是平滑系数:(一般可以忽略)
|-weights=1.0, 损失计算结果加权
|-label_smoothing=0, 标签平滑
'''
INPUT_SIZE=4
OUTPUT_SIZE=1
# 我们使用鸢尾花数据作为分类测试
# 1. 定义数据(一般是样本与样本标签)
x=tf.placeholder(dtype=tf.float32,shape=[None,INPUT_SIZE])
y=tf.placeholder(dtype=tf.float32,shape=[None,OUTPUT_SIZE])
w=tf.random_uniform(shape=(INPUT_SIZE,OUTPUT_SIZE),minval=-0.01,maxval=0.01,dtype=tf.float32)
b=tf.random_uniform(shape=[OUTPUT_SIZE],minval=-0.01,maxval=0.01,dtype=tf.float32)
# 2. 决策参数模型描述
o_y=tf.matmul(x,w)+b
o_predict=tf.sigmoid(o_y)
# 3. 梯度下降参数模型
loss=ls.sigmoid_cross_entropy(y,o_y) # y是输入的真实标签,o_y是决策模型输出的理想标签
# 4. tensorflow运算执行会话环境
op_init=tf.initializers.global_variables()
session=tf.Session()
session.run(op_init)
# 5. 执行与训练
# 加载输入(样本特征)与输出数据(实际分类的标签)
data,target=datasets.load_iris(return_X_y=True)
data=data[50:150]
target=target[0:100]
# 对target坐下形状格式处理
target=target.reshape(target.shape[0],OUTPUT_SIZE)
o_loss=session.run(loss, feed_dict={x:data,y:target})
print(o_loss)
# 6. 预测与分类评估
0.7235981
(8)定义最小值优化算法
import numpy as np
import tensorflow as tf
import tensorflow.math as ma
import tensorflow.losses as ls
import tensorflow.train as tr
from sklearn import datasets
'''
定义训练优化器:
梯度下降优化器:tf.train.GradientDescentOptimizer
__init__(
learning_rate,
use_locking=False,
name='GradientDescent'
)
参数:
learning_rate:学习率
# 优化器训练目标与方向
minimize函数:
minimize(
loss,
global_step=None,
var_list=None,
gate_gradients=GATE_OP,
aggregation_method=None,
colocate_gradients_with_ops=False,
name=None,
grad_loss=None
)
其他优化器:
tf.train.AdamOptimizer:可以自动调整下降速度
tf.train.AdadeltaOptimizer
tf.train.AdagradDAOptimizer
tf.train.AdagradOptimizer
'''
INPUT_SIZE=4
OUTPUT_SIZE=1
# 我们使用鸢尾花数据作为分类测试
# 1. 定义数据(一般是样本与样本标签)
x=tf.placeholder(dtype=tf.float32,shape=[None,INPUT_SIZE])
y=tf.placeholder(dtype=tf.float32,shape=[None,OUTPUT_SIZE])
v_w=tf.random_uniform(shape=(INPUT_SIZE,OUTPUT_SIZE),minval=-0.01,maxval=0.01,dtype=tf.float32)
v_b=tf.random_uniform(shape=[OUTPUT_SIZE],minval=-0.01,maxval=0.01,dtype=tf.float32)
# -------------------
# 训练变量必须是Variable 变量
w=tf.Variable(v_w)
b=tf.Variable(v_b)
# -------------------
# 2. 决策参数模型描述
o_y=tf.matmul(x,w)+b
o_predict=tf.sigmoid(o_y)
# 3. 梯度下降参数模型
loss=ls.sigmoid_cross_entropy(y,o_y) # y是输入的真实标签,o_y是决策模型输出的理想标签
optimizer=tr.GradientDescentOptimizer(learning_rate=0.0001)
print(optimizer)
trainer=optimizer.minimize(loss=loss)
print(trainer)
# 4. tensorflow运算执行会话环境
op_init=tf.initializers.global_variables()
session=tf.Session()
session.run(op_init)
# 5. 执行与训练
# 加载输入(样本特征)与输出数据(实际分类的标签)
data,target=datasets.load_iris(return_X_y=True)
data=data[50:150]
target=target[0:100]
# 对target坐下形状格式处理
target=target.reshape(target.shape[0],OUTPUT_SIZE)
o_trainer=session.run(trainer, feed_dict={x:data,y:target})
print(o_trainer) # 返回none
# 6. 预测与分类评估
<tensorflow.python.training.gradient_descent.GradientDescentOptimizer object at 0x127883e48>
name: "GradientDescent_26"
op: "NoOp"
input: "^GradientDescent_26/update_Variable_70/ApplyGradientDescent"
input: "^GradientDescent_26/update_Variable_71/ApplyGradientDescent"
None
(9)预测与分类
import numpy as np
import tensorflow as tf
import tensorflow.math as ma
import tensorflow.losses as ls
import tensorflow.train as tr
from sklearn import datasets
'''
'''
INPUT_SIZE=4
OUTPUT_SIZE=1
# 我们使用鸢尾花数据作为分类测试
# 1. 定义数据(一般是样本与样本标签)
x=tf.placeholder(dtype=tf.float32,shape=[None,INPUT_SIZE])
y=tf.placeholder(dtype=tf.float32,shape=[None,OUTPUT_SIZE])
init_w=tf.random_uniform(shape=(INPUT_SIZE,OUTPUT_SIZE),minval=-0.1,maxval=0.1,dtype=tf.float32)
init_b=tf.random_uniform(shape=[OUTPUT_SIZE],minval=-0.1,maxval=0.1,dtype=tf.float32)
w=tf.Variable(init_w)
b=tf.Variable(init_b)
# 2. 决策参数模型描述
o_y=tf.matmul(x,w)+b
o_predict=tf.sigmoid(o_y)
# 3. 梯度下降参数模型
loss=ls.sigmoid_cross_entropy(y,o_y) # y是输入的真实标签,o_y是决策模型输出的理想标签
optimizer=tr.GradientDescentOptimizer(learning_rate=0.001)
trainer=optimizer.minimize(loss=loss)
# 4. tensorflow运算执行会话环境
op_init=tf.initializers.global_variables()
session=tf.Session()
session.run(op_init)
# 5. 执行与训练
data,target=datasets.load_iris(return_X_y=True)
data=data[50:150]
target=target[0:100]
target=target.reshape(target.shape[0],OUTPUT_SIZE)
# 训练次数
# 分epoch,每个epoch分若干batch,每个batch若干样本
epoch=10000
batch_size=10
batch_num=10
for t in range(epoch):
for i in range(batch_num):
session.run(trainer, feed_dict={x:data[i*batch_size:(i+1)*batch_size],y:target[i*batch_size:(i+1)*batch_size]})
# 训练误差低于某个值就结束训练
ls=session.run(loss, feed_dict={x:data,y:target})
if ls < 10e-5:
print("梯度过小,结束训练!")
break
# 6. 预测与分类评估
o_v=session.run(o_predict, feed_dict={x:data})
#print(o_v)
# 统计正确率
# 前50个
a_num=(o_v[0:50]<0.5).sum()
print(a_num)
b_num=(o_v[50:100]>0.5).sum()
print(b_num)
47
50
九、使用numpy原生实现逻辑回归
- 原生实现的几个核心
|-
|-梯度计算公式:
|- |-
|-
|-梯度更新公式:
|- |-
|- |- S函数一个比较好的性质就是其导数可以用自己表示
-
损失函数与激活函数的导数
|-
|-损失函数的导数:
|- |- 某些时候可以加一个因子
|- |-
|- |-
|- |-
|-
|-激活函数的导数:
|- |- S函数一个比较好的性质就是其导数可以用自己表示 -
【补充】自然对数求导
|-自然函数:
|-导数: -
原生实现逻辑结构
【注意】:我们在计算的时候,就不是单个的标量数据,而是标量数据形成的向量数据或者矩阵运算。
【注意】:这里我们还是使用鸢尾花数据集。
import numpy as np
from sklearn import datasets
# 条件准备
# 计算线性运算
# 计算决策输出
# 计算梯度
# 更新梯度
# 执行训练
# 运算评估
- 原生实现代码参考
(1)面向对象结构设计
import numpy as np
from sklearn import datasets
# 优化器
class GDOptimizer:
def __init__(self):
# 条件准备
pass
def forward(self):
# 计算线性运算
# 计算决策输出
pass
def backwrad(self):
# 计算梯度
# 更新梯度
def optimize(self):
# 一次输出计算,一次梯度更新就是一次优化,多次优化形成训练
pass
def predict(self):
# 优化后可以对数据形成预测或者分类
pass
# 训练器
class Trainer:
optimizer=GDOptimizer()
# 执行训练
def train(self):
pass
def evaluate(self):
# 运算评估
pass
class LogisticRegressionApp:
trainer=Trainer()
def run(self):
#执行逻辑回归
pass
(2)数据设计
import numpy as np
from sklearn import datasets
# 优化器
class GDOptimizer:
def __init__(self, learning_rate):
# 学习率
self.learning_rate=learning_rate
# sigmoid函数与导数
self.sigmoid=None
self.sigmoid_derivatives=None
# 损失函数与导数
self.sigmoid_cross_entropy=None
self.sigmoid_cross_entropy_derivatives=None
# 训练的权重与偏置项
self.weight=None
self.bias=None
def forward(self, data):
# 训练样本
self.data=data #输入
# 计算线性运算
self.o_linear=None #输出
# 计算决策输出
self.o_sigmoid=None #输出
def backward(self,target):
# 已实际分类的标签(或者实际预测值)
self.target=target #输入
# 计算梯度
self.weight_delta=None #输出
self.bias_delta=None #输出
# 更新梯度
self.weight_grad=None #输出
self.bias_grad=None #输出
def optimize(self, data, target):
# 一次输出计算,一次梯度更新就是一次优化,多次优化形成训练
print('计算输出')
self.forward(data)
print('计算梯度并更新')
self.backward(target)
def predict(self,data):
# 优化后可以对数据形成预测或者分类
self.forward(data)
return self.o_sigmoid
# 训练器
class Trainer:
def __init__(self,learning_rate,epoches):
# 训练率
self.learning_rate=learning_rate
# 训练代(轮)数
self.epoches=epoches
# 训练器
self.optimizer=GDOptimizer(self.learning_rate)
# 加载数据
self.data=None
self.target=None
# 执行训练
def train(self):
for ep in range(self.epoches):
print('第%d训练'%(ep+1))
self.optimizer.optimize(self.data, self.target)
def evaluate(self):
# 运算评估
self.pre=self.optimizer.predict(data)
# 对结果进行分析,并输出
class LogisticRegressionApp:
def __init__(self):
self.trainer=Trainer(0.001, 5)
def run(self):
print('开始训练')
print('-------------------')
#执行逻辑回归
self.trainer.train()
print('-------------------')
print('开始评估')
self.trainer.evaluate()
app=LogisticRegressionApp()
app.run()
开始训练
-------------------
第1训练
计算输出
计算梯度并更新
第2训练
计算输出
计算梯度并更新
第3训练
计算输出
计算梯度并更新
第4训练
计算输出
计算梯度并更新
第5训练
计算输出
计算梯度并更新
-------------------
开始评估
(3)计算输出
|-
|-某些时候可以加一个因子,从后面的验证中看出tensorflow中加上了这个因子。我们也采用。
|- |-
|-
|-梯度计算公式:
|- |-
|-
|-梯度更新公式:
|- |-
import numpy as np
from sklearn import datasets
# 优化器
class GDOptimizer:
def __init__(self, learning_rate):
# 学习率
self.learning_rate=learning_rate
# sigmoid函数与导数
self.sigmoid=lambda x:1.0/(1+np.exp(-x))
self.sigmoid_derivatives=lambda x: self.sigmoid(x) * (1-self.sigmoid(x) )
# 损失函数与导数(暂时不考虑均值与正负符号)
self.sigmoid_cross_entropy=lambda y1, y2: y1*np.log(y2)+(1-y1)*np.log(1-y2)
self.sigmoid_cross_entropy_derivatives=lambda y1,y2:(y1-y2)/(y2*(1-y2))
# 训练的权重与偏置项
self.weight=np.random.uniform(low=-0.1,high=0.1,size=(4,1)) # 这里最好使用参数决定
self.bias=np.random.uniform(low=-0.1,high=0.1,size=(1,1))
def forward(self, data):
# 训练样本
self.data=data #输入
# 计算线性运算
self.o_linear=np.matmul(self.data, self.weight)+self.bias #输出
# 计算决策输出
self.o_sigmoid=self.sigmoid(self.o_linear) #输出
def backward(self,target):
# 已实际分类的标签(或者实际预测值)
self.target=target #输入
# 计算误差
self.err=-self.sigmoid_cross_entropy(target,self.o_sigmoid).mean(axis=0)
#print('误差:',self.err)
# 计算梯度
v1=self.sigmoid_cross_entropy_derivatives(self.target,self.o_sigmoid)
v2=self.sigmoid_derivatives(self.o_linear)
v=v1*v2
self.weight_delta=-(v*self.data).mean(axis=0) #输出
self.bias_delta=-v.mean(axis=0) #输出
#print(self.weight_delta.shape)
#print(self.bias_delta.shape)
# 更新梯度
self.weight_grad=self.learning_rate*self.weight_delta #输出
self.bias_grad=self.learning_rate*self.bias_delta #输出
self.weight_grad=self.weight_grad.reshape(self.weight.shape)
self.bias_grad=self.bias_grad.reshape(self.bias.shape)
self.weight=self.weight-self.weight_grad
self.bias-=self.bias_grad
def optimize(self, data, target):
# 一次输出计算,一次梯度更新就是一次优化,多次优化形成训练
#print('计算输出')
self.forward(data)
#print('计算梯度并更新')
self.backward(target)
def predict(self,data):
# 优化后可以对数据形成预测或者分类
self.forward(data)
return self.o_sigmoid
# 训练器
class Trainer:
def __init__(self,learning_rate,epoches):
# 训练率
self.learning_rate=learning_rate
# 训练代(轮)数
self.epoches=epoches
# 训练器
self.optimizer=GDOptimizer(self.learning_rate)
# 加载数据
# --------------------------------------------------------
self.data,self.target=datasets.load_iris(return_X_y=True)
self.data=self.data[50:150]
self.target=self.target[0:100]
# 注意重新reshape形状
self.target=self.target.reshape((len(self.target),1))
# --------------------------------------------------------
# 执行训练
def train(self):
for ep in range(self.epoches):
#print('第%d训练'%(ep+1))
self.optimizer.optimize(self.data, self.target)
def evaluate(self):
# 运算评估
self.pre=self.optimizer.predict(self.data)
#print(self.pre)
# 对结果进行分析,并输出
a_cls=self.pre[0:50]
b_cls=self.pre[50:100]
a_num=(a_cls<0.5).sum()
b_num=(b_cls>0.5).sum()
print("A类识别正确数:%d"%(a_num))
print("B类识别正确数:%d"%(b_num))
class LogisticRegressionApp:
def __init__(self):
self.trainer=Trainer(0.02, 100000) # 调整这两个参数,观察误差值变化与识别正确数。
def run(self):
print('开始训练')
print('-------------------')
#执行逻辑回归
self.trainer.train()
print('-------------------')
print('开始评估')
self.trainer.evaluate()
app=LogisticRegressionApp()
app.run()
开始训练
-------------------
-------------------
开始评估
A类识别正确数:48
B类识别正确数:49
【附录】sigmoid与交叉熵函数验证
import numpy as np
from sklearn import datasets
import tensorflow as tf
# sigmoid函数与导数
sigmoid=lambda x:1.0/(1+np.exp(-x))
sigmoid_derivatives=lambda x: self.sigmoid(x) * (1-self.sigmoid(x) )
# 损失函数与导数(与交叉熵差一点:sigmoid输出)
sigmoid_cross_entropy=lambda y1, y2: -np.mean(y1*np.log(y2)+(1-y1)*np.log(1-y2), axis=0)
sigmoid_cross_entropy_derivatives=lambda y1,y2:-np.mean((y1-y2)/(y2*(1-y2)),axis=1)
# 交叉熵(这里的交叉熵实际上计算了均值)
sigmoid_cross_entropy_1=lambda y1, y2: -np.mean(y1*np.log(sigmoid(y2))+(1-y1)*np.log(1-sigmoid(y2)), axis=0)
# 执行环境
op_init=tf.initializers.global_variables()
session=tf.Session()
session.run(op_init)
# sigmoid函数验证
print("sigmoid函数")
print('\t|-Numpy:',sigmoid(1.0))
r1=tf.sigmoid(1.0)
print('\t|-Tensorflow:',session.run(r1))
# 损失函数验证
#y1=np.array([[0.5]])
#y2=np.array([[0.5]])
y1=np.array([[0.5],[0.3],[0.8]])
y2=np.array([[0.5],[0.7],[0.2]])
print("损失函数(无sigmoid计算)")
print('\t|-损失函数:',sigmoid_cross_entropy(y1,y2))
print("交叉熵函数")
print('\t|-Numpy:',sigmoid_cross_entropy_1(y1,y2))
r2=tf.losses.sigmoid_cross_entropy(y1,y2)
print('\t|-Tensorflow:',session.run(r2))
sigmoid函数
|-Numpy: 0.7310585786300049
|-Tensorflow: 0.7310586
损失函数
|-损失函数: [0.99170322]
交叉熵函数
|-Numpy: [0.75180063]
|-Tensorflow: 0.7518007159233093
仿照使用numpy原生实现方法,可以很轻松实现线性回归的梯度算法。原生实现的难点在于多个样本数据处理就会要注意计算细节,尤其是会动用到对矩阵的理解。