Python 被称为是最接近 AI 的语言。最近一位名叫Anna-Lena Popkes(德国波恩大学计算机科学专业的研究生,主要关注机器学习和神经网络)的小姐姐在GitHub上分享了自己如何使用Python(3.6及以上版本)实现7种机器学习算法的笔记,并附有完整代码。所有这些算法的实现都没有使用其他机器学习库。这份笔记可以帮大家对算法以及其底层结构有个基本的了解,但并不是提供最有效的实现。
Softmax 回归算法,又称为多项式或多类别的 Logistic 回归算法。
给定:
-
数据集
-
是d-维向量
-
对应于
的目标变量,例如对于K=3分类问题,
Softmax 回归模型有以下几个特点:
-
对于每个类别,都存在一个独立的、实值加权向量
- 这个权重向量通常作为权重矩阵中的行。
- 对于每个类别,都存在一个独立的、实值偏置量b
- 它使用 softmax 函数作为其激活函数
- 它使用交叉熵( cross-entropy )作为损失函数
训练 Softmax 回归模型有不同步骤。首先(在步骤0中),模型的参数将被初始化。在达到指定训练次数或参数收敛前,重复以下其他步骤。
第 0 步:用 0 (或小的随机值)来初始化权重向量和偏置值
第 1 步:对于每个类别k,计算其输入的特征与权重值的线性组合,也就是说为每个类别的训练样本计算一个得分值。对于类别k,输入向量为,则得分值的计算如下:
其中表示类别k的权重矩阵,·表示点积。
我们可以通过矢量化和矢量传播法则计算所有类别及其训练样本的得分值:
其中 X 是所有训练样本的维度矩阵,W 表示每个类别的权重矩阵维度,其形式为
;
第 2 步:用 softmax 函数作为激活函数,将得分值转化为概率值形式。属于类别 k 的输入向量的概率值为:
同样地,我们可以通过矢量化来对所有类别同时处理,得到其概率输出。模型预测出的表示的是该类别的最高概率。
第 3 步:计算整个训练集的损失值。
我们希望模型预测出的高概率值是目标类别,而低概率值表示其他类别。这可以通过以下的交叉熵损失函数来实现:
在上面公式中,目标类别标签表示成独热编码形式( one-hot )。因此为1时表示
的目标类别是 k,反之则为 0。
第 4 步:对权重向量和偏置量,计算其对损失函数的梯度。
关于这个导数实现的详细解释,可以参见这里(http://ufldl.stanford.edu/tutorial/supervised/SoftmaxRegression/)。
一般形式如下:
对于偏置量的导数计算,此时为1。
第 5 步:对每个类别k,更新其权重和偏置值。
其中,表示学习率。
In [1]:
from sklearn.datasets import load_iris
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
np.random.seed(13)
数据集
In [2]:
X, y_true = make_blobs(centers=4, n_samples = 5000)
fig = plt.figure(figsize=(8,6))
plt.scatter(X[:,0], X[:,1], c=y_true)
plt.title("Dataset")
plt.xlabel("First feature")
plt.ylabel("Second feature")
plt.show()
In [3]:
# reshape targets to get column vector with shape (n_samples, 1)
y_true = y_true[:, np.newaxis]
# Split the data into a training and test set
X_train, X_test, y_train, y_test = train_test_split(X, y_true)
print(f'Shape X_train: {X_train.shape}')
print(f'Shape y_train: {y_train.shape}')
print(f'Shape X_test: {X_test.shape}')
print(f'Shape y_test: {y_test.shape}')
Shape X_train: (3750, 2)
Shape y_train: (3750, 1)
Shape X_test: (1250, 2)
Shape y_test: (1250, 1)
Softmax回归分类
class SoftmaxRegressor:
def __init__(self):
pass
def train(self, X, y_true, n_classes, n_iters=10, learning_rate=0.1):
"""
Trains a multinomial logistic regression model on given set of training data
"""
self.n_samples, n_features = X.shape
self.n_classes = n_classes
self.weights = np.random.rand(self.n_classes, n_features)
self.bias = np.zeros((1, self.n_classes))
all_losses = []
for i in range(n_iters):
scores = self.compute_scores(X)
probs = self.softmax(scores)
y_predict = np.argmax(probs, axis=1)[:, np.newaxis]
y_one_hot = self.one_hot(y_true)
loss = self.cross_entropy(y_one_hot, probs)
all_losses.append(loss)
dw = (1 / self.n_samples) * np.dot(X.T, (probs - y_one_hot))
db = (1 / self.n_samples) * np.sum(probs - y_one_hot, axis=0)
self.weights = self.weights - learning_rate * dw.T
self.bias = self.bias - learning_rate * db
if i % 100 == 0:
print(f'Iteration number: {i}, loss: {np.round(loss, 4)}')
return self.weights, self.bias, all_losses
def predict(self, X):
"""
Predict class labels for samples in X.
Args:
X: numpy array of shape (n_samples, n_features)
Returns:
numpy array of shape (n_samples, 1) with predicted classes
"""
scores = self.compute_scores(X)
probs = self.softmax(scores)
return np.argmax(probs, axis=1)[:, np.newaxis]
def softmax(self, scores):
"""
Tranforms matrix of predicted scores to matrix of probabilities
Args:
scores: numpy array of shape (n_samples, n_classes)
with unnormalized scores
Returns:
softmax: numpy array of shape (n_samples, n_classes)
with probabilities
"""
exp = np.exp(scores)
sum_exp = np.sum(np.exp(scores), axis=1, keepdims=True)
softmax = exp / sum_exp
return softmax
def compute_scores(self, X):
"""
Computes class-scores for samples in X
Args:
X: numpy array of shape (n_samples, n_features)
Returns:
scores: numpy array of shape (n_samples, n_classes)
"""
return np.dot(X, self.weights.T) + self.bias
def cross_entropy(self, y_true, scores):
loss = - (1 / self.n_samples) * np.sum(y_true * np.log(scores))
return loss
def one_hot(self, y):
"""
Tranforms vector y of labels to one-hot encoded matrix
"""
one_hot = np.zeros((self.n_samples, self.n_classes))
one_hot[np.arange(self.n_samples), y.T] = 1
return one_hot
初始化并训练模型
regressor = SoftmaxRegressor()
w_trained, b_trained, loss = regressor.train(X_train, y_train, learning_rate=0.1, n_iters=800, n_classes=4)
fig = plt.figure(figsize=(8,6))
plt.plot(np.arange(800), loss)
plt.title("Development of loss during training")
plt.xlabel("Number of iterations")
plt.ylabel("Loss")
plt.show()Iteration number: 0, loss: 1.393
Iteration number: 100, loss: 0.2051 Iteration number: 200, loss: 0.1605 Iteration number: 300, loss: 0.1371 Iteration number: 400, loss: 0.121 Iteration number: 500, loss: 0.1087 Iteration number: 600, loss: 0.0989 Iteration number: 700, loss: 0.0909
测试模型
n_test_samples, _ = X_test.shape
y_predict = regressor.predict(X_test)
print(f"Classification accuracy on test set: {(np.sum(y_predict == y_test)/n_test_samples) * 100}%")
测试集分类准确率:99.03999999999999%
注意:本文归作者所有,未经作者允许,不得转载