机器学习-初级入门(分类算法-逻辑回归、softmax详解)

一、逻辑回归实现二分类

  1. sigmod函数
    g(z) = 1 / (1 + e-z)

    在这里插入图片描述

  2. 替换推导

    令z = WTX则g(z) = 1 / (1 + e-)因为g(z)函数的特性,它输出的结果也不再是预测结果,而是一个值预测为正例的概率,预测为负例的概率就是1-g(z)。
    函数形式表达:

    P(y=0|w,x) = 1 – g(z)
    P(y=1|w,x) =  g(z)
    

    在这里插入图片描述
    sigmod函数预测结果为一个0到1之间的小数,选定阈值的第一反应,大多都是选0.5,其实实际工作中并不一定是0.5,阈值的设定往往是根据实际情况来判断的。本小节我们只举例让大家理解为什么不完全是0.5,并不会有一个万能的答案,都是根据实际工作情况来定的。

  3. 最大似然估计求损失函数

    若想让预测出的结果全部正确的概率最大,根据最大似然估计,就是所有样本预测正确的概率相乘得到的P(总体正确)最大,此时我们让在这里插入图片描述数学表达形式如下:
    在这里插入图片描述
    通过两边同时取log的形式让其变成连加.
    在这里插入图片描述得到的这个函数越大,证明我们得到的W就越好.因为在函数最优化的时候习惯让一个函数越小越好,所以我们在前边加一个负号.得到公式如下:
    在这里插入图片描述
    这个函数就是我们逻辑回归(logistics regression)的损失函数,我们叫它交叉熵损失函数.

  4. 求解交叉熵损失函数
    求解步骤如下:

    1. 随机一组W.
    2. 将W带入交叉熵损失函数,让得到的点沿着负梯度的方向移动.
    3. 循环第二步.
    

    求解梯度部分同样是对损失函数求偏导,过程如下:
    在这里插入图片描述交叉熵损失函数的梯度和最小二乘的梯度形式上完全相同,区别在于,此时的
    在这里插入图片描述,而最小二乘的在这里插入图片描述

  5. 代码实现:

    from sklearn.preprocessing import LabelEncoder, OneHotEncoder
    from sklearn.metrics import confusion_matrix, accuracy_score
    from matplotlib.colors import ListedColormap
    import matplotlib.pyplot as plt
    import pandas as pd
    import numpy as np
    import random
    
    
    def dataset_format(filename):
        """
        划分训练测试集
        :param filename:文件路径
        :return:
        """
        dataset = pd.read_csv(filename)
        # M = dataset["Gender"] == "Male"
        # dataset["Gender"] = M
        train_data = dataset.sample(frac=0.75, random_state=100, axis=0)
        test_data = dataset[~dataset.index.isin(train_data.index)]
        X_train = train_data.iloc[:, 2:-1].values
        y_train = train_data.iloc[:, -1].values
        X_test = test_data.iloc[:, 2:-1].values
        y_test = test_data.iloc[:, -1].values
        return X_train, y_train, X_test, y_test
    
    
    def initialize_weights(X):
        """
        初始化权重
        :param X:
        :return:
        """
        # 初始化参数
        # 参数范围[-1/sqrt(N), 1/sqrt(N)]
        n_features = X.shape[1]
        limit = np.sqrt(1 / n_features)
        w = np.random.uniform(-limit, limit, (n_features, 1))
        w = np.insert(w, 0, 0, axis=0)  # 加入偏值
        return w
    
    
    def sigmoid(x):
        """
        sigmoid函数
        :param x:
        :return:
        """
        return 1 / (1 + np.exp(-x))
    
    
    def stander(X):
        """
        标准化数据,特征缩放
        :param X:
        :return:
        """
        X = np.mat(X, dtype=float)
        # regularize
        x_mean = np.mean(X, 0)
        x_std = np.std(X, 0)
        np.seterr(divide="ignore", invalid='ignore')  # xVar中存在0元素
        # 特征标准化: (特征-均值)/方差
        X = (X - x_mean) / x_std
        return X
    
    
    def fit(X, y, learning_rate=0.001, n_iterations=500):
        """
        梯度下降训练数据
        :param X: 训练集自变量
        :param y: 训练集因变量
        :param learning_rate: 学习率
        :param n_iterations: 迭代次数
        :return: 最优权重
        """
        W = initialize_weights(X)
        # 为X增加一列特征x1,x1 = 0
        X = np.insert(X, 0, 1, axis=1)
        y = np.reshape(y, (len(X), 1))
        # 梯度训练n_iterations轮
        for i in range(n_iterations):
            h_x = X.dot(W)
            h_x = np.mat(h_x, dtype=float)
            y_pred = sigmoid(h_x)
            w_grad = X.T.dot(y_pred - y)
            W = W - learning_rate * w_grad
        return W
    
    
    def predict(X, w):
        """
        预测测试数据
        :param X: 测试集自变量
        :param w: 最优权重
        :return:
        """
        X = np.insert(X, 0, 1, axis=1)
        h_x = X.dot(w)
        h_x = np.mat(h_x, dtype=float)
        y_pred = np.round(sigmoid(h_x))
        return y_pred.astype(int)
    
    
    X_train, y_train, X_test, y_test = dataset_format("Social_Network_Ads.csv")
    X_train = stander(X_train)
    X_test = stander(X_test)
    W = fit(X_train, y_train)
    y_test_hat = predict(X_test, W)
    y_train_hat = predict(X_train, W)
    
    cm = confusion_matrix(y_test, y_test_hat)
    accuracy = accuracy_score(y_test, y_test_hat)
    X_set, y_set = X_test, y_test
    X1, X2 = np.meshgrid(np.arange(start=X_set[:, 0].min() - 1, stop=X_set[:, 0].max() + 1, step=0.01),
                         np.arange(start=X_set[:, 1].min() - 1, stop=X_set[:, 1].max() + 1, step=0.01))
    plt.contourf(X1, X2, predict(np.array([X1.ravel(), X2.ravel()]).T, W).reshape(X1.shape),
                 alpha=0.75, cmap=ListedColormap(('red', 'green')))
    plt.xlim(X1.min(), X1.max())
    plt.ylim(X2.min(), X2.max())
    for i, j in enumerate(np.unique(y_set)):
        plt.scatter(X_set[y_set == j, 0].tolist(), X_set[y_set == j, 1].tolist(),
                    c=ListedColormap(('orange', 'blue'))(i), label=j)
    plt.title('Logistic Regression (Test set)')
    plt.xlabel('Age')
    plt.ylabel('Estimated Salary')
    plt.legend()
    plt.show()
    

    分类结果:
    在这里插入图片描述

二、多分类实现(softmax)

  1. softmax的定义

    在这里插入图片描述
    假设有一个数组V,Vi表示V中的第i个元素,那么这个元素的softmax值为:
    在这里插入图片描述

  2. 交叉熵(softmax的损失函数):
    交叉熵,其用来衡量在给定的真实分布下,使用非真实分布所指定的策略消除系统的不确定性所需要付出的努力的大小。使用交叉熵做分类问题中的损失函数,可以在一定程度上减少梯度消散。
    softmax中使用的交叉熵公式如下:
    在这里插入图片描述
    在softmax中,ti表示真实值,yi表示求出的softmax值,输入一个样本,那么只有一个神经元对应了该样本的正确类别;若这个神经元输出的概率值越高,则按照以上的函数公式,其产生的损失就越小;反之,则产生的损失就越高。
    举个简单的计算例子:
    在这里插入图片描述

  3. 对损失函数进行求导

    当预测第i个时,可以认为ti=1。此时损失函数变成了
    在这里插入图片描述
    对Loss求导。根据定义
    在这里插入图片描述
    将数值映射到了0-1之间,并且和为1,则有
    在这里插入图片描述
    具体求导过程
    在这里插入图片描述
    通过上面的求导结果,可以发现结果恰好为通过softmax函数求出了概率减1,那么我们就可以得到反向更新的梯度了。
    举个计算的例子:

    通过若干层的计算,最后得到的某个训练样本的向量的分数是[ 2, 3, 4 ],
    那么经过softmax函数作用后概率分别就是 [0.0903, 0.2447, 0.665],
    如果这个样本正确的分类是第二个的话,
    那么计算出来的偏导就是[0.0903, 0.2447-1, 0.665]=[0.0903, -0.7553, 0.665](划重点),
    然后再根据这个进行back propagation就可以了。
    
  4. 代码实现
    用到的公式:
    在这里插入图片描述

    def dataset_split(filename):
        """
        划分数据集
        :param filename:
        :return:
        """
        dataset = pd.read_csv(filename)
        # M = dataset["Gender"] == "Male"
        # dataset["Gender"] = M
        train_data = dataset.sample(frac=0.75, random_state=100, axis=0)
        test_data = dataset[~dataset.index.isin(train_data.index)]
        X_train = train_data.iloc[:, :-1].values
        y_train = train_data.iloc[:, -1].values
        X_test = test_data.iloc[:, :-1].values
        y_test = test_data.iloc[:, -1].values
        return X_train, y_train, X_test, y_test
    
    
    def label_encoder(y):
        """
        对标签进行编码
        :param y:
        :return:
        """
        label_encoder_y = LabelEncoder()
        y = label_encoder_y.fit_transform(y)
        return np.mat(y).T
    
    
    def cal_e(x, w, i):
        """
        公式三: e^(θi.xi)
        :param x: 随机的某行样本特征, 需转化成 特征个数行 x 1列
        :param w: 分类个数行k x 特征个数列
        :param i: 第i个类别
        :return: 生成 1 x 1 矩阵
        """
        theata_l = w[i]  # 1行 x 特征个数列
        return np.exp(theata_l.dot(x.T))
    
    
    def cal_probability(x, w, k, i):
        """
        公式二: P(yi = j|xi;θ) = e^(θi.xi) / ∑(j=1~k) e^(θj.xi)
        :param x: 随机的某行样本特征
        :param w: 权值向量
        :param k: k个分类
        :param i: 第i个分类
        :return: 某个特征的比率
        """
        molecule = cal_e(x, w, i)  # 1行 x 1列
        denominator = sum([cal_e(x, w, j) for j in range(k)])
        return molecule / denominator
    
    
    def cal_partial_derivative(x, y, w, k, i, weight_lambda):
        """
        公式一: △θjJ(θ) = -xi * (1{yi = j} - P(yi = j|xi;θ)) + λθ
        :param x: 随机的某行样本特征
        :param y: 随机的某行样本标签
        :param w: 分类个数行k x 特征个数列
        :param k: k个分类
        :param i: 第i个分类
        :param weight_lambda: 衰退权重
        :return: △θjJ(θ)
        """
        first = (y[0, 0] == i)  # 计算示性函数
        second = cal_probability(x, w, k, i)[0, 0]
        return -x * (first - second) + weight_lambda * w[i]
    
    
    def softmax_train(X, y, max_iteration=5000, weight_lambda=0.01, learning_step = 0.1):
        """
        :param X: 特征
        :param y: 标签
        :param k: 几种类型标签
        :return:
        """
        X = np.insert(X, 0, 1, axis=1)
        k = y.max() + 1
        w = np.zeros((k, X.shape[1]))
        random_max = y.shape[0] - 1
        w = np.mat(w)
        for time in range(max_iteration):
            index = random.randint(0, random_max)
            l_x = X[index]
            l_y = y[index]
            derivatives = [cal_partial_derivative(l_x, l_y, w, k, j, weight_lambda) for j in range(k)]
            for c in range(k):
                w[c] -= learning_step * derivatives[c]
        return w
    
    
    def softmax_predict(X, w):
        X = np.insert(X, 0, 1, axis=1)
        r, c = X.shape
        y_t = np.zeros((r, 1))
        for i in range(r):
            x_T = X[i]
            result = w.dot(x_T.T)
            positon = result.argmax()
            y_t[i] = positon
        return y_t
    
    
    X_train, y_train, X_test, y_test = dataset_split("iris.csv")
    X_train = stander(X_train)
    X_test = stander(X_test)
    y_train = label_encoder(y_train)
    y_test = label_encoder(y_test)
    
    w = softmax_train(X_train, y_train)
    y_test_hat = softmax_predict(X_test, w)
    
    cm = confusion_matrix(y_test, y_test_hat)
    accuracy = accuracy_score(y_test, y_test_hat)
    
    print("混淆矩阵\n", cm)
    print("准确度: ", accuracy)
    

    输出结果:

    混淆矩阵
     [[ 8  0  0]
     [ 0 19  0]
     [ 0  1  9]]
    准确度:  0.972972972972973
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值