对数机率回归(逻辑回归)

逻辑回归

  1.  逻辑回归的模型是一个非线性模型
  2.  sigmoid函数,又称逻辑回归函数。但是它本质上又是一个线性回归模型
  3.  因为除去sigmoid映射函数关系,其他的步骤,算法都是线性回归
  4.  可以说,逻辑回归,都是以线性回归为理论支持的
  5.  只不过,线性模型,无法做到sigmoid的非线性形式,sigmoid可以轻松处理0/1分类问题

 逻辑回归
 

  1.  找一个合适的预测函数,一般表示为h函数,该函数就是需要找的分类函数,它用来预测输入数据的判断结果
  2.  构造一个损失函数,该函数表示预测输出(h)与训练数据类别(y)之间的偏差,可以是二者之间的差(h-y)或者其他的形式。综合考虑所有训练数据的“损失”,将损失求和或者求平均,记为J(\theta )函数,表示所有训练数据预测值与实际类别的偏差。
  3. 显然,J(\theta )函数的值越小表示预测函数越精确,所以这一步需要做的就是找到J(\theta )函数的最小值,这里利用梯度下降法

 Logistic函数

    如果忽略二分类问题中的y的取值是一个离散的取值(0或1),我们继续使用线性回归来预测y的取值。这样做会导致y的取值并不为0或1。逻辑回归使用一个函数来归一化y值,使y的取值在区间(0,1)内,这个函数称为Logistic函数,也称Sigmoid函数,函数公式如下:

函数图形如下:

Z是线性回归函数,所以也可以表示成:

对于线性边界的情况,边界形式如下:

构造预测函数为:

函数h_{\theta }(x)的值有特殊的含义,它表示结果取1的概率,因此对于输入x分类结果为类别1和类别0的概率分别为:

损失函数

对于任何机器学习问题,都需要明确损失函数:

但是在LR模型解决的二分类问题中,这个函数形式为:

这个损失函数通常称作是对数损失(logloss),由于y的取值为0或者1,因此可以表示为:

优化求解

现在我们已经确定了模型的损失函数,那么接下来就是根据这个损失函数,不断优化模型参数从而获得拟合数据的最佳模型。重新看一下损失函数,其本质上是;关于模型中线性方程部分两个参数w和b的函数:

其中:

z=w^{T}x+b

现在的学习任务转化为数学优化的形式即为:

由于损失函数连续可微,我们可以借助梯度下降法进行优化求解,对于这个核心参数的更新方式如下:

 

代码及代码解释如下:

import numpy as np
class LogisticRegression(object):
#初始化参数(学习率,最大迭代次数,seed为种子数,seed相同时,numpy.random.seed()生成的随机数相同
    def __init__(self,learning_rate=0.1,max_iter=100,seed=None):
        self.seed = seed
        self.lr = learning_rate
        self.max_iter = max_iter
#进行训练,np.random.normal()的意思是一个正态分布,normal这里是正态的意思。
# 参数loc(float):正态分布的均值,对应着这个分布的中心。loc=0说明这一个以Y轴为对称轴的正态分布,
# 参数scale(float):正态分布的标准差,对应分布的宽度,scale越大,正态分布的曲线越矮胖,scale越小,曲线越高瘦。
# 参数size(int 或者整数元组):输出的值赋在shape里,默认为None。
    def fit(self,x,y):
        np.random.seed(self.seed)
        self.w = np.random.normal(loc=0.0,scale=1.0,size=x.shape[1])
        self.b = np.random.normal(loc=0.0,scale=1.0)
        self.x = x
        self.y = y
        for i in range(self.max_iter):
            self._update_step()
    def _sigmoid(self,z):
        return 1.0/(1.0+np.exp(-z))
# 返回一个逻辑函数
    def _f(self,x,w,b):
        z = x.dot(w) + b
        return self._sigmoid(z)
    def predict_prob(self,x=None):
        if x is None:
            x = self.x
        y_preb = self._f(x,self.w,self.b)
# 预测值,y_pred_proba表示逻辑函数Y,是一个概率,y_pred为分类后的预测值
    def predict(self,x=None):
        if x is None:
            x = self.x
        y_pred_proba = self._f(x,self.w,self.b)
        y_pred = np.array([0 if y_pred_proba[i]<0.5 else 1 for i in range(len(y_pred_proba))])
        return y_pred
    def score(self,y_true=None,y_pred=None):
        if y_true is None or y_pred is None:
            y_true = self.y
            y_pred = self.predict()
        acc = np.mean([1 if y_true[i]==y_pred[i] else 0 for i in range(len(y_true))])
        return acc
#损失函数
    def loss(self,y_true=None,y_pred_proba=None):
        if y_true is None or y_pred is None:
            y_true = self.y
            y_pred_proba = self.predict_proba()
        return np.mean(-1.0*(y_true*np.log(y_pred_prob)+(1.0-y_true)*np.log(1.0-y_pred_proba)))
# 机算梯度
    def _cal_gradient(self):
        y_pred = self.predict()
        d_w = (y_pred - self.y).dot(self.x)/len(self.y)
        d_b = np.mean(y_pred - self.y)
        return d_w,d_b
# 进行迭代,利用梯度下降算法求解w,b,d_w,d_b为损失函数L对w,b求偏导
    def _update_step(self):
        d_w,d_b = self._cal_gradient()
        self.w = self.w - self.lr*d_w
        self.b = self.b - self.lr*d_b
        return self.w, self.b
#生成数据
def generate_data(seed):
    np.random.seed(seed)
    data_size_1 = 300

    x1_1 = np.random.normal(loc=5.0,scale=1.0,size=data_size_1)
    x2_1 = np.random.normal(loc=5.0,scale=1.0,size=data_size_1)
    y_1 = [0 for _ in range(data_size_1)]

    data_size_2 = 400
    x1_2 = np.random.normal(loc=10.0,scale=2.0,size=data_size_2)
    x2_2 = np.random.normal(loc=8.0,scale=2.0,size=data_size_2)
    y_2 = [1 for _ in range(data_size_2)]
# numpy.concatenate((a1, a2, ...), axis),a1,a2 .. 相同类型的数组序列,axis 必须连接数组的轴。默认值为0
    x1 = np.concatenate((x1_1,x1_2),axis=0)
    x2 = np.concatenate((x2_1,x2_2),axis=0)
# hstack(tup) ,参数tup可以是元组,列表,或者numpy数组,返回结果为numpy的数组,它其实就是水平(按列顺序)把数组给堆叠起来
# reshape(-1,1)表示(任意行,1列),第一列为横座标,第二列为纵座标,y为分类的值,为0或者1
    x = np.hstack((x1.reshape(-1,1),x2.reshape(-1,1)))
    y = np.concatenate((y_1,y_2),axis=0)
# np.random.permutation():随机排列序列                
    data_size_all = data_size_1 + data_size_2
    shuffled_index = np.random.permutation(data_size_all)
    x = x[shuffled_index]
    y = y[shuffled_index]
    return x,y
# 训练集和测试集分割
def train_tset_split(x,y):
    splite_index = int(len(y)*0.7)
    x_train = x[:splite_index]
    y_train = y[:splite_index]
    x_test = x[splite_index:]
    y_test = y[splite_index:]
    return x_train,y_train,x_test,y_test
import matplotlib.pyplot as plt
x,y = generate_data(seed=272)
x_train,y_train,x_test,y_test = train_tset_split(x,y)
# x_train第一列为横座标,第二列为纵座标,根据y_train的值(0或者1)来标记颜色
plt.scatter(x_train[:,0],x_train[:,1],c=y_train,marker=".")
plt.show()
plt.scatter(x_test[:,0], x_test[:,1], c=y_test, marker='.')
plt.show()
# s数据归一化
x_train=(x_train-np.min(x_train,axis=0))/(np.max(x_train,axis=0)-np.min(x_train,axis=0))
x_test=(x_test-np.min(x_test,axis=0))/(np.max(x_test,axis=0)-np.min(x_test,axis=0))
# 逻辑回归
clf = LogisticRegression(learning_rate=0.1,max_iter=500,seed=272)
clf.fit(x_train,y_train)
# xx表示0.1-0.6 步长为0.1
# 由于是斜向下的直线,因此y=-wx-b
split_boundary_fuc = lambda x:(-clf.b-clf.w[0]*x)/clf.w[1]
xx = np.arange(0.1,0.6,0.1)
cValue=['g','b']
plt.scatter(x_train[:,0],x_train[:,1],c=[cValue[i] for i in y_train],marker="o")
plt.plot(xx,split_boundary_fuc(xx),c='red')

运行结果为:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章