Press "Enter" to skip to content

机器学习算法之Softmax Regression

本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.

由于 Logistic Regression算法 复杂度低、容易实现等特点,在工业界中得到广泛使用,如计算广告中的点击率预估等。但是,Logistic Regression算法主要是用于处理二分类问题,若需要处理的是多分类问题,如手写字识别,即识别是{0,1,…,9}中的数字,此时,需要使用能够处理多分类问题的算法。Softmax Regression算法是Logistic Regression算法在多分类问题上的推广,主要用于处理多分类问题,其中,任意两个类之间是线性可分的。

 

Logistic Regression

 

在Logistic回归中比较重要的有两个公式,一个是阶跃函数:

 

 

另一个是对应的损失函数:

 

 

最终,Logistic回归需要求出的是两个概率: 。具体的Logistic回归的过程可参见“ 机器学习算法之逻辑回归 ”。

 

 

在Logistic回归需要求解的是两个概率,而在Softmax Regression中将不是两个概率,而是k个概率,k表示的是分类的个数。我们需要求出以下的概率值:

 

 

此时的损失函数为:

 

 

其中I{}是一个指示性函数,意思是大括号里的值为真时,该函数的结果为1,否则为0。下面就这几个公式做个解释:

 

损失函数的由来

 

概率函数可以表示为:

 

 

其似然函数为:

 

 

似然为:

 

 

我们要最大化似然函数,即求 。再转化成损失函数。

 

似然(或者是损失函数)求偏导

 

为了简单,我们仅取一个样本,则可简单表示为:

 

 

求偏导:

 

 

其中,表示第维。如Logistic回归中一样,可以使用基于梯度的方法来求解这样的最大化问题。

 

使用Python实现Softmax Regression

 

import numpy as np
import random
 
 
def load_train_data(input_file):
    feature_data = []
    label_data = []
    with open(input_file) as f:
        for line in f.readlines():
            feature_tmp = []
            feature_tmp.append(1)  # 偏置项
            lines = line.strip().split("\t")
            for i in range(len(lines) - 1):
                feature_tmp.append(float(lines[i]))
            label_data.append(int(lines[-1]))
            feature_data.append(feature_tmp)
    return np.mat(feature_data), np.mat(label_data).T, len(set(label_data))
 
 
def load_test_data(num, m):
    """导入测试数据
    :param num: 生成的测试样本的个数
    :param m: 样本的维数
    :return: 生成测试样本
    """
    test_dataset = np.mat(np.ones((num, m)))
    for i in range(num):
        test_dataset[i, 1] = random.random() * 6 - 3  # 随机生成[-3,3]之间的随机数
        test_dataset[i, 2] = random.random() * 15  # 随机生成[0,15]之间是的随机数
    return test_dataset
 
 
def cost(err, label_data):
    """计算损失函数值
    :param err: exp的值
    :param label_data: 标签的值
    :return: sum_cost / m(float):损失函数的值
    """
    m = np.shape(err)[0]
    sum_cost = 0.0
    for i in range(m):
        if err[i, label_data[i, 0]] / np.sum(err[i, :]) > 0:
            sum_cost -= np.log(err[i, label_data[i, 0]] / np.sum(err[i, :]))
        else:
            sum_cost -= 0
    return sum_cost / m
 
 
def gradient_ascent(feature_data, label_data, k, max_cycle, alpha):
    """利用梯度下降法训练Softmax模型
    :param feature_data: 特征
    :param label_data: 标签
    :param k: 类别的个数
    :param max_cycle: 最大的迭代次数
    :param alpha: 学习率
    :return: weights(mat):
    """
    m, n = np.shape(feature_data)
    weights = np.mat(np.ones((n, k)))  # 权重的初始化
    i = 0
    while i <= max_cycle:
        err = np.exp(feature_data * weights)
        if i % 500 == 0:
            print("\t-----iter: ", i, ", cost: ", cost(err, label_data))
        row_sum = -err.sum(axis=1)
        row_sum = row_sum.repeat(k, axis=1)
        err = err / row_sum
        for x in range(m):
            err[x, label_data[x, 0]] += 1
        weights = weights + (alpha / m) * feature_data.T * err
        i += 1
    return weights
 
 
def save_weights(file_name, weights):
    """保存最终的模型
    :param file_name: 保存的文件名
    :param weights: softmax模型
    :return:
    """
    f_w = open(file_name, "w")
    m, n = np.shape(weights)
    for i in range(m):
        w_tmp = []
        for j in range(n):
            w_tmp.append(str(weights[i, j]))
        f_w.write("\t".join(w_tmp) + "\n")
    f_w.close()
 
 
def load_weights(weights_path):
    """导入训练好的Softmax模型
    :param weights_path: 权重的存储位置
    :return: weights(mat)将权重存到矩阵中
            m(int)权重的行数
            n(int)权重的列数
    """
    with open(weights_path) as f:
        w = []
        for line in f.readlines():
            w_tmp = []
            lines = line.strip().split("\t")
            for x in lines:
                w_tmp.append(float(x))
            w.append(w_tmp)
    weights = np.mat(w)
    m, n = np.shape(weights)
    return weights, m, n
 
 
def predict(test_data, weights):
    """利用训练好的Softmax模型对测试数据进行预测
    :param test_data: 测试数据的特征
    :param weights: 模型的权重
    :return: h.argmax(axis=1)所属的类别
    """
    h = test_data * weights
    return h.argmax(axis=1)  # 获得所属的类别
 
 
def save_result(file_name, result):
    """保存最终的预测结果
    :param file_name: 保存最终结果的文件名
    :param result: 最终的预测结果
    :return:
    """
    with open(file_name, "w") as f:
        m = np.shape(result)[0]
        for i in range(m):
            f.write(str(result[i, 0]) + "\n")
 
 
if __name__ == "__main__":
    input_file = "./data/SoftInput.txt"
    feature, label, k = load_train_data(input_file)
    weights = gradient_ascent(feature, label, k, 10000, 0.4)  # 训练Softmax模型
    save_weights("weights", weights)
    w, m, n = load_weights("weights")
    test_data = load_test_data(4000, m)
    result = predict(test_data, w)
    save_result("result", result)

 

使用scikit-learn中的Softmax Regression

 

softmax和LogisticRegression分别适用于多分类和二分类问题,sklearn将这两者放在一起,只需设置相应的参数即可选择分类器与对应的优化算法,需要注意的是loss function是否收敛。

 

from sklearn.linear_model import LogisticRegression
 
clf = LogisticRegression(multi_class='ovr', solver='sag')
clf.fit(X_train, y_train)
r = clf.score(X_test, y_test)

 

参考链接: https://towardsdatascience.com/logistic-regression-the-good-parts-55efa68e11df

Be First to Comment

发表评论

邮箱地址不会被公开。 必填项已用*标注