逻辑回归

ML03-逻辑回归(下部分)

2018-12-07  本文已影响13人  杨强AT南京

本文主题-逻辑回归(下部分):

  1. 逻辑回归的应用背景
  2. 逻辑回归的数学基础
  3. 逻辑回归的模型与推导
  4. 逻辑回归算法推导
  5. 梯度下降算法
  6. 逻辑回归梯度下降算法实现

七、线性回归sklearn实现

  1. 采用极端样本分类
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
  1. 可视化训练结果
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]
极端样本分类可视化
  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'
  1. 采用鸢尾花样本(第一类与第二类者第三类)
      该样本的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%
  1. 采用鸢尾花样本(第二类与第三类)
      该样本的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. 梯度下降法几个条件
      |-决策计算模型
        |-y_i=S(X_iW)
      
      |-损失函数(版本一)
        |- J(W)=-ln(L(W))=-\sum\limits_{i}y_i\ ln(h(X_i)) +(1-y_i)\ln(1-h(X_i))
      |-损失函数(版本二)有的为了计算的方便会增加一个\dfrac{1}{m}因子,看起来像求均值。
        |- J(W)=-\dfrac{1}{m}ln(L(W))=-\dfrac{1}{m} \sum\limits_{i}^{m} y_i\ ln(h(X_i)) +(1-y_i)\ln(1-h(X_i))
  1. 梯度下降通用推导
      |-梯度计算(通用版)
        |-\nabla_W J(W)= \dfrac{ \partial J(W) }{\partial W}
      |-根据链式偏导可以分解求导参数的链式分解:
        |-\nabla_W J(W)= \dfrac{ \partial J(W) }{\partial y_i}\ \dfrac{\partial y_i}{\partial W}
        |-\nabla_W J(W)= \dfrac{ \partial J(W) }{\partial y_i}\ \dfrac{\partial y_i}{\partial X_iW}\ \dfrac{\partial X_iW}{\partial W}
        |-\nabla_W J(W)= \dfrac{ \partial J(W) }{\partial y_i}\ \dfrac{\partial y_i}{\partial X_iW}\ X_i
      |-这种通用的梯度推导结构由三个因素决定:
        |-(1)损失函数的导数
        |-(2)输出控制函数的导数
        |-(3)输入特征
      |-通常把前面两项称为误差项\delta
        |- \delta=\dfrac{ \partial J(W) }{\partial y_i}\ \dfrac{\partial y_i}{\partial X_iW}
      |-可以使用导数表示误差项\delta
        |- \delta=J^{\prime}(\bar{y_i})\ S^\prime(X_iW)
      
      注意:这里我们使用了一个符号\bar{y_i}用来区分y_i\bar{y_i}表示理想输出值,表示样本实际输出结果。
      
        |- \nabla_W J(W) =\eta\ \delta\ X_i 其中\eta用来控制梯度下降速度
      
      梯度更新公式:
        |-W_{new}=W_{old} - \eta\ \nabla_W J(W)

  2. 梯度计算的验证
      由于梯度核心计算就是误差项的计算,该值容易计算错误,如果大家理解误差项的计算方式,很容易找到另外一种验证方式(也可以做为导数的近似计算方式 ),就是损失函数的导数,实际是另外一种极限的表示:
        |-f^\prime(x)=\lim\limits_{\epsilon \to 0} \dfrac{f(x+\epsilon)-f(x-\epsilon)}{2\epsilon}
        |-f^\prime(x) \approx \dfrac{f(x+\epsilon)-f(x-\epsilon)}{2\epsilon} 当然\epsilon必须任意小

  3. 线性回归中的梯度下降关键公式表示
      
      |-损失函数:J(W)= \dfrac{1}{2} \sum \limits _{i=1}^{m}( \bar{y^{(i)}}-y^{(i)})^2   某些时候可以加一个\dfrac{1}{m}因子
      
      |-决策模型:f(x)=x
      
      |-线性模型:y=xW

  4. 逻辑回归中的梯度下降关键公式表示
      
      |-损失函数: J(W)=-ln(L(W))=-\sum\limits_{i=1}^{m}y_i\ ln(\bar{y_i}) +(1-y_i)\ln(1-\bar{y_i})  某些时候可以加一个\dfrac{1}{m}因子
      
      |-决策模型:f(x)=\dfrac{1}{1+e^{-x}}   S函数一个比较好的性质就是其导数可以用自己表示f\prime(x)=f(x)(1-f(x))
      
      |-线性模型:y=xW
      
      我们称在线性模型之上的运算函数为激活函数(这是神经网络的用语)

  5. 关于线性回归与逻辑回归的共性
      从上面的梯度下降法中可以看出,线性回归与逻辑回归只是在损失函数与决策模型输出有差异,但还是遵循一个线性计算。 所以都是广义上的线性回归模型。(这种共性在神经网络中被统一起来,而且还有很多其他的决策模型与损失函数,再神经网络中,决策模型称为激活函数,激活函数决定着输出特征在下一层特征融合中的重要性 )

  6. 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原生实现逻辑回归

  1. 原生实现的几个核心
      |-
      |-梯度计算公式:
      |-  |-\nabla_W J(W)= \dfrac{ \partial J(W) }{\partial y_i}\ \dfrac{\partial y_i}{\partial X_iW}\ X_i
      |-
      |-梯度更新公式:
      |-  |-W_{new}=W_{old} - \eta\ \nabla_W J(W)

  |-  |-S(x)=\dfrac{1}{1+e^{-x}}   S函数一个比较好的性质就是其导数可以用自己表示S^\prime(x)=S(x)(1-S(x))
  

  1. 损失函数与激活函数的导数
      |-
      |-损失函数的导数:
      |-  |-J(\bar{y_i})=-ln(L(\bar{y_i}))=-\sum\limits_{i=1}^{m}y_i\ ln(\bar{y_i}) +(1-y_i)\ln(1-\bar{y_i})  某些时候可以加一个\dfrac{1}{m}因子
      |-  |-J^\prime(\bar{y_i})=-\sum\limits_{i=1}^{m}(\dfrac{y_i}{\bar{y_i}} - \dfrac{1-y_i}{1-\bar{y_i}})
      |-  |-J^\prime(\bar{y_i})=-\sum\limits_{i=1}^{m}\dfrac{y_i-y_i\bar{y_i}-\bar{y_i}+\bar{y_i}y_i}{\bar{y_1}(1-\bar{y_i})}
      |-  |-J^\prime(\bar{y_i})=-\sum\limits_{i=1}^{m}\dfrac{y_i-\bar{y_i}}{\bar{y_1}(1-\bar{y_i})}
      |-
      |-激活函数的导数:
      |-  |-S(x)=\dfrac{1}{1+e^{-x}}   S函数一个比较好的性质就是其导数可以用自己表示S^\prime(x)=S(x)(1-S(x))

  2. 【补充】自然对数求导
      |-自然函数:f(x)=lnx
      |-导数:f ^\prime(x)=\dfrac{1}{x}

  3. 原生实现逻辑结构
      【注意】:我们在计算的时候,就不是单个的标量数据,而是标量数据形成的向量数据或者矩阵运算。
      【注意】:这里我们还是使用鸢尾花数据集。

import numpy as np
from sklearn import datasets

# 条件准备

# 计算线性运算

# 计算决策输出

# 计算梯度

# 更新梯度

# 执行训练

# 运算评估

  1. 原生实现代码参考

  (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)计算输出
    |-J(\bar{y_i})=-ln(L(\bar{y_i}))=-\sum\limits_{i=1}^{m}y_i\ ln(\bar{y_i}) +(1-y_i)\ln(1-\bar{y_i})
    |-某些时候可以加一个\dfrac{1}{m}因子,从后面的验证中看出tensorflow中加上了这个因子。我们也采用。
    |-  |-J^\prime(\bar{y_i})=-\sum\limits_{i=1}^{m}\dfrac{y_i-\bar{y_i}}{\bar{y_1}(1-\bar{y_i})}

  |-
  |-梯度计算公式:
  |-  |-\nabla_W J(W)= \dfrac{ \partial J(W) }{\partial y_i}\ \dfrac{\partial y_i}{\partial X_iW}\ X_i
  |-
  |-梯度更新公式:
  |-  |-W_{new}=W_{old} - \eta\ \nabla_W J(W)

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原生实现方法,可以很轻松实现线性回归的梯度算法。原生实现的难点在于多个样本数据处理就会要注意计算细节,尤其是会动用到对矩阵的理解。

上一篇下一篇

猜你喜欢

热点阅读