机器学习算法之Logistic Regression

Logistic Regression

1.逻辑回归的介绍

逻辑回归算法是一种分类算法,是典型的监督学习,其训练样本包含样本的特征和标签信息,在二分类中,标签为离散值,如{+1,-1},分别表示正类和负类。分类算法通过对训练样本的学习,得到从样本特征到样本标签之间的映射关系,也被称为假设函数,之后可利用该假设函数对新数据进行分类。

2.逻辑回归模型

机器学习算法之Logistic Regression

逻辑回归模型是广义线性模型的一种,属于线性的分类模型。对于图中的线性可分的问题,需要找到一条直线,能够将俩个不同的类分开。这条直线称为超平面,可以使用如下的线性函数表示
Wx+b=0Wx+b = 0
其中W为权重,b为偏置。多维情况下,W,b均为向量

3.阈值函数

为了将数据分成正负两个类别,可以使用阈值函数,将样本映射到不同类别中,常见的阈值函数有Sigmoid函数,其形式如下:
f(x)=11+exf(x) = {{1} \over {1+e^{-x}}}
图像为:
机器学习算法之Logistic Regression

对于输入向量X,其属于正例的概率为
P(y=1X,W,b)=σ(WX+b)=11+e(WX+b)P(y=1|X,W,b) = \sigma(WX+b)= {{1} \over {1+e^{-(WX+b)}}}
属于负例的概率
P(y=0X,W,b)=1P(y=1X,W,b)=1σ(WX+b)=e(WX+b)1+e(WX+b)P(y=0|X,W,b)=1-P(y=1|X,W,b) =1- \sigma(WX+b) = {{e^{-(WX+b)}} \over {1+e^{-(WX+b)}}}

4.损失函数

对于逻辑回归算法,其属于类别y的概率:
P(yX,W,b)=σ(WX+b)y(1σ(WX+b))1yP(y | X, W, b)=\sigma(W X+b)^{y}(1-\sigma(W X+b))^{1-y}
为了求参数W和b,可以使用极大似然法对其进行估计,其似然函数为:
LW,b=i=1m[hW,b(X(i))y(i)(1hW,b(X(i)))1y(i)]L_{W, b}=\prod_{i=1}^{m}\left[h_{W, b}\left(X^{(i)}\right)^{y^{(i)}}\left(1-h_{W, b}\left(X^{(i)}\right)\right)^{1-y^{(i)}}\right]
其中
hW,b(X(i))=σ(WX(i)+b)h_{W, b}\left(X^{(i)}\right)=\sigma\left(W X^{(i)}+b\right)
对于似然函数的极大值的求解,通常使用Log似然函数,在逻辑回归算法中,通常是将负的Log似然函数作为其损失函数,损失函数为:
lw,b=1mlogLw,bl_{w,b}=-{{1}\over{m}}logL_{w,b}
此时,我们需要求minlw,bl_{w,b},可以使用基于梯度的方法求解

5.梯度下降法

在机器学习算法中,对于很多监督学习模型,需要对原始的模型构建损失函数l,接下来便是通过优化算法对损失函数l进行优化,以便寻找到最优的参数W。在求解机器学习参数W的优化算法时,使用较多的是基于梯度下降的优化算法(GD)。

梯度下降法的含义是通过当前点的梯度方向寻找到新的迭代点,并从当前点移动到新的迭代点继续寻找新的迭代点,直到找到最优解。该方法的优点:在求解过程中,只需求解损失函数的一阶导数,计算成本比较小。

梯度下降法的过程:
  1. 随机选择一个初始点w0w_0

  2. 重复以下过程

    • 决定梯度下降的方向:di=wf(w)wid_{i}=-\frac{\partial}{\partial w} f\left.(w)\right|_{w_{i}}
    • 选择步长α\alpha
    • 更新:wi+1=wi+αdiw_{i+1} = w_i + \alpha*d_i
  3. 直到满足终止条件
    机器学习算法之Logistic Regression

6.梯度的求解

梯度为:
Wj(lW,b)=1mi=1m(y(i)hW,b(X(i)))xj(i)\nabla_{W_{j}}\left(l_{W, b}\right)=-\frac{1}{m} \sum_{i=1}^{m}\left(y^{(i)}-h_{W, b}\left(X^{(i)}\right)\right) x_{j}^{(i)}
b(lW,b)=1mi=1m(y(i)hW,b(X(i)))\nabla_{b}\left(l_{W, b}\right)=-\frac{1}{m} \sum_{i=1}^{m}\left(y^{(i)}-h_{W, b}\left(X^{(i)}\right)\right)
其中,xj(i)x_{j}^{(i)}表示的是样本X(i)X^{(i)}的第j个分量。取 w0=bw_0=b,且将偏置项的变量x0x_0设为1,则可将上述的梯度合并为:
W,(lW,b)=1mi=1m(y(i)hW,b(X(i)))xj(i)\nabla_{W,}\left(l_{W, b}\right)=-\frac{1}{m} \sum_{i=1}^{m}\left(y^{(i)}-h_{W, b}\left(X^{(i)}\right)\right) x_{j}^{(i)}
根据梯度下降法,得到如下的更新公式:
Wj=Wj+αWj(lW,b)W_{j}=W_{j}+\alpha \nabla_{W_{j}}\left(l_{W, b}\right)

7.步长α\alpha的选择

对于步长α\alpha的选择,若选择太小,会导致收敛速度比较慢;若选择太大,则会出现震荡的现象,即跳过最优解,在最优解附近徘徊

8.逻辑回归算法实践

利用训练样本训练逻辑回归模型

'''
Date:20190405
@author:codecat
'''
import numpy as np
import matplotlib.pyplot as plt
def load_data(file_name):
    '''导入训练数据
    input: file_name(string)训练数据的位置
    output: feature_data(mat)特征
    label_data(mat)标签
    '''
    f = open(file_name,'r') # 打开文件
    feature_data = []
    label_data = []
    for line in f.readlines():
        feature_tmp = []
        lable_tmp = []
        lines = line.strip().split("\t")
        feature_tmp.append(1) # 偏置项
        for i in range(len(lines) - 1):
            feature_tmp.append(float(lines[i]))
        lable_tmp.append(float(lines[-1]))
        feature_data.append(feature_tmp)
        label_data.append(lable_tmp)
    f.close() # 关闭文件
    return np.mat(feature_data), np.mat(label_data)
    
def sig(x):
    '''Sigmoid函数
    input: x(mat):feature * w
    output: sigmoid(x)(mat):Sigmoid值
    '''
    return 1.0 / (1 + np.exp(-x))
    
def lr_train_bgd(feature, label, maxCycle, alpha):
    '''利用梯度下降法训练LR模型
    input: feature(mat)特征
    label(mat)标签
    maxCycle(int)最大迭代次数
    alpha(float)学习率
    output: w(mat):权重
    '''
    n = np.shape(feature)[1] # 特征个数
    w = np.mat(np.ones((n, 1))) # 初始化权重
    i = 0
    while i <= maxCycle: # 在最大迭代次数的范围内
        i += 1 # 当前的迭代次数
        h = sig(feature * w) # 计算Sigmoid值
        err = label - h
        if i % 100 == 0:
            print ("\t---------iter=" + str(i) + " , train error rate= " + str(error_rate(h, label)))
        w = w + alpha * feature.T * err # 权重修正
    return w
    
def error_rate(h, label):
    '''计算当前的损失函数值
    input: h(mat):预测值
    label(mat):实际值
    output: err/m(float):错误率
    '''
    m = np.shape(h)[0]
    sum_err = 0.0
    for i in range(m):
        if h[i, 0] > 0 and (1 - h[i, 0]) > 0:
                sum_err -= (label[i,0] * np.log(h[i,0]) +  (1-label[i,0]) * np.log(1-h[i,0]))
        else:
                 sum_err -= 0
    return sum_err / m
    
def save_model(file_name, w):
    '''保存最终的模型
    input: file_name(string):模型保存的文件名
    w(mat):LR模型的权重
    '''
    m = np.shape(w)[0]
    f_w = open(file_name, "w")
    w_array = []
    for i in range(m):
            w_array.append(str(w[i, 0]))
    f_w.write("\t".join(w_array))
    f_w.close()
    
def draw(weight,file_name):
    x0List=[]
    y0List=[]
    x1List=[]
    y1List=[]
    f=open(file_name,'r')
    for line in f.readlines():
        lines=line.strip().split()
        if lines[2]=='0':
            x0List.append(float(lines[0]))
            y0List.append(float(lines[1]))
        else:
            x1List.append(float(lines[0]))
            y1List.append(float(lines[1]))
            
    fig=plt.figure()
    ax=fig.add_subplot(111)
    ax.scatter(x0List,y0List,s=10,c='red')
    ax.scatter(x1List,y1List,s=10,c='green')
    
    xList=[];yList=[]
    x=np.arange(-3,13,0.1)
    for i in range(len(x)):
        xList.append(x[i])
    y=(-weight[0]-weight[1]*x)/weight[2]
    for j in range(y.shape[1]):
        yList.append(y[0,j])
    ax.plot(xList,yList)
    plt.xlabel('x1');plt.ylabel('x2')
    plt.show()
    
if __name__ == "__main__":
# 1、导入训练数据
    print ("---------- 1.load data ------------")
    feature, label = load_data("data.txt")
    # 2、训练LR模型
    print ("---------- 2.training ------------")
    w = lr_train_bgd(feature, label, 1000, 0.01)
    # 3、保存最终的模型
    print ("---------- 3.save model ------------")
    save_model("weights", w)
    # 4,绘制图像
    draw(w,'data.txt')

机器学习算法之Logistic Regression

对新数据进行预测


'''
Date:20190405
@author:codecat
'''
import numpy as np
from lr_train import sig

def load_weight(w):
    '''导入LR模型
    input: w(string)权重所在的文件位置
    output: np.mat(w)(mat)权重的矩阵
    '''
    f = open(w)
    w = []
    for line in f.readlines():
        lines = line.strip().split("\t")
        w_tmp = []
        for x in lines:
            w_tmp.append(float(x))
        w.append(w_tmp)
    f.close()
    print(w)
    return np.mat(w)
    
def load_data(file_name, n):
    '''导入测试数据
    input: file_name(string)测试集的位置
    n(int)特征的个数
    output: np.mat(feature_data)(mat)测试集的特征
    '''
    f = open(file_name)
    feature_data = []
    for line in f.readlines():
        feature_tmp = []
        lines = line.strip().split("\t")
        if len(lines) != n - 1:
            continue
        feature_tmp.append(1)
        for x in lines:
            feature_tmp.append(float(x))
        feature_data.append(feature_tmp)
    f.close()
    return np.mat(feature_data)
    
def predict(data, w):
    '''对测试数据进行预测
    input: data(mat)测试数据的特征
    w(mat)模型的参数
    output: h(mat)最终的预测结果
    '''
    h = sig(data * w.T)#sig
    m = np.shape(h)[0]
    for i in range(m):
        if h[i, 0] < 0.5:
            h[i, 0] = 0.0
        else:
            h[i, 0] = 1.0
    return h
    
def save_result(file_name, result):
    '''保存最终的预测结果
    input: file_name(string):预测结果保存的文件名
    result(mat):预测的结果
    '''
    m = np.shape(result)[0]
    #输出预测结果到文件
    tmp = []
    for i in range(m):
        tmp.append(str(result[i, 0]))
    f_result = open(file_name, "w")
    f_result.write("\n".join(tmp))
    f_result.close()

if __name__ == "__main__":
    # 1、导入LR模型
    print ("---------- 1.load model ------------")
    w = load_weight("weights")
    n = np.shape(w)[1]
    print(n)
    # 2、导入测试数据
    print ("---------- 2.load data ------------")
    testData = load_data("test_data", n)
    # 3、对测试数据进行预测
    print ("---------- 3.get prediction ------------")
    h = predict(testData, w)#进行预测
    # 4、保存最终的预测结果
    print ("---------- 4.save prediction ------------")
    save_result("result", h)