引言:对于学习AI来说,先入门机器学习很重要,因为深度学习中一些概念,比如sigmoid激活函数等都是从机器学习中提出的。线性回归只能解决线性问题,比如预测房价等,但不能解决离散问题,比如分类,逻辑回归能解决二分类问题。
原理及推导
1. 逻辑回归模型是从广义线性模型的来的,广义线性模型是线性模型左边加上联系函数。
如原线性回归模型公式为:
如果加上ln 作为联系函数则变成
2. 对数几率模型
几率:几率在统计学中是指一件事发生的概率与不发生概率的比值.公式如下:
对数几率则是加上了log,如下
那么我们可以构建一个函数:
化简可得:
最后得到sigmoid函数:
sigmoid:
(以上和X都为矩阵)
得到的sigmoid函数,y始终在0-1之间,所以可以作二分类问题,大于某个阈值为1类,小于则为0类。一般取0.5为阈值,因为sigmoid函数在y = 0.5处梯度最大,可以理解为两个类别在此时变化很大。
接下来就是构建损失函数,因为sigmoid函数如果使用RMSE均方根误差是非凸函数,很难找到最小值,有很多局部最小值,所以不适合,所以sigmoid函数构建了以下的
最大似然loss函数。
然后使用传统的梯度下降对求偏导不断逼近loss最小值
即梯度下降公式为: (设m个样本,为第i个特征,为m个样本的特征组成的特征矩阵)
构建logistic回归代码和线性回归的逻辑是一样的
数据集方面,使用了kaggle上的Dry_Bean数据集
数据集下载:Dry Bean Dataset (kaggle.com)
因为简单的逻辑回归只能解决二分类问题,所以我们可以取0到3349个样本
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from sklearn.model_selection import train_test_split
data = pd.read_csv('Dry_Bean_Dataset.csv')
df = pd.DataFrame(data)
color = []
for i in df['Class'][0:3349]:
if i == 'SEKER':
color.append('red')
else:
color.append('blue')
plt.scatter(df['MajorAxisLength'][0:3349], df['MinorAxisLength'][0:3349], color=color)
plt.xlabel('MajorAxisLength')
plt.ylabel('MinorAxisLength')
data_np = np.array(data)[0:3349]
# 取出特征和标签
X = data_np[:, :-1]
Y = data_np[:, -1:]
# X = np.concatenate((X, np.ones(shape=(np.shape(X)[0], 1))), axis=1) # 将截距放到一个矩阵中
# _w = np.random.randn(np.shape(X)[0]+1, 1)
# X_test = np.random.randn(1, 17)
# X_test = np.concatenate((X_test, np.ones(shape=(1, 1))), axis=1)
# X = np.concatenate((X, np.ones(shape=(np.shape(X)[0], 1))), axis=1)
"""
使用sk-learn的train_test 划分训练集和测试集,也可以自己写一个函数
"""
X_train, X_test, Y_train, Y_test = train_test_split(X, Y)
print(np.shape(X_test))
class LogisticRegression:
"""
_w : 系数,后面会让截距和系数一起合并
_isfit : 判断是否训练过一次
optimizer : 优化器
"""
def __int__(self):
self._w = None
self.optimizer = 'bgd'
self.isfit = False
def normalization(self,x_train:np.ndarray, x_test:np.ndarray):
x_train = x_train.astype(float)
mean = np.mean(x_train, axis=0)
std = np.std(x_train, axis=0)
x_train = (x_train-mean)/std
x_test = (x_test-mean)/std
return x_train, x_test
def _bgd(self):
return
def _sigmoid(self, x):
x = np.array(x)
return 1. / (1. + np.exp(-x))
def lr_schedule(self, epoch):
return 0.98 ** epoch
"""
对数据进行归一化处理,以训练集为参考,将训练集 测试集都进行归一化处理
使用的是z-core方法
"""
def z_score_normalization(self, X, X_Test):
mean = np.mean(X, axis=0)
std = np.std(X, axis=0)
X_normalized = (X - mean) / std
X_Test_normalized = (X_Test - mean) / std
return X_normalized, X_Test_normalized
def fit(self, x_train: np.ndarray, y_train: np.ndarray, x_test: np.ndarray, y_test: np.ndarray, lr=0.0001,
batchsize=50, iters=20000, optimizer='bgd'):
'''
:param x_train: 特征,n行表示n个数据
:param y_train: 标签
:param lr: 学习率
:param batchsize: SBGD梯度下降的 批量
:param iters: 迭代次数
:return:
'''
n_samples = x_train.shape[0]
n_features = x_train.shape[1]
labels = np.unique(y_train) # 取出标签名,然后将标签转化为0或1方便
for i in range(len(y_train)):
if y_train[i] == labels[0]:
y_train[i] = 0
else:
y_train[i] = 1
for i in range(len(y_test)):
if y_test[i] == labels[0]:
y_test[i] = 0
else:
y_test[i] = 1
self.optimizer = optimizer
x_train, x_test = self.normalization(x_train, x_test)
x_train = np.concatenate((x_train, np.ones(shape=(np.shape(x_train)[0], 1))), axis=1).astype(float)
x_test = np.concatenate((x_test, np.ones(shape=(np.shape(x_test)[0], 1))), axis=1).astype(float)
self._w = np.random.randn(x_train.shape[1], 1) # 初始化权重 # 初始化截距
# print(type(np.dot(x_train, self._w)))
"""
先进行归一化数据
使用BGD梯度下降
和线性回归一样,先初始化系数,然后使用梯度下降逼近最优解
SGD还没测试
"""
for _ in range(iters):
if self.optimizer == 'bgd': # BGD批量梯度下降,取全部样本
"""
因为梯度下降过程中_w系数可能会超出float 浮点数能表示的范围
所以要限制位数
"""
liner_model = np.dot(x_train, self._w).astype(np.float32)
y_predict = self._sigmoid(liner_model)
# 更新权重
dw = np.dot(x_train.T, (y_predict - y_train))*(1/n_features)
if dw.all() < 0.000001:
break
self._w = self._w - lr * dw
lr = lr * self.lr_schedule(_)
elif self.optimizer == 'sgd':
idx = np.random.randint(0, n_samples)
x_idx, y_idx = x_train[idx], y_train[idx]
y_predict = self._sigmoid(np.dot(x_idx, self._w))
dw = x_idx(y_predict - y_idx)
self._w -= lr * dw
lr = lr * self.lr_schedule(1)
self.isfit = True
return self.predict(x_test)
"""
输入一组测试数据集
每次取出一个样本进行预测
"""
def predict(self, X, threshold=0.5):
if not self.isfit:
print('not fit')
return
n_samples = X.shape[0]
# X = np.concatenate((X, np.ones(shape=(n_samples, 1))), axis=1)
y_predict = []
for i in range(len(X)):
liner_model = np.dot(X[i], self._w).astype(np.float32)
y = self._sigmoid(liner_model)
print(y)
y_predict.append(1 if y > threshold else 0)
# y_predict = self._sigmoid(liner_model)
# print(y_predict)
#print(liner_model)
return y_predict
LogisticNet = LogisticRegression()
Y_predict = LogisticNet.fit(x_train=X_train, x_test=X_test, y_train=Y_train, y_test=Y_test)
labels =np.unique(Y_train)
"""
测试准确率
"""
for i in range(len(Y_test)):
if Y_test[i] == labels[0]:
Y_test[i] = 0
else:
Y_test[i] = 1
acc = 0
err = 0
for i in range(len(Y_test)):
if Y_predict[i] == Y_test[i]:
acc += 1
else:
err += 1
print(Y_test)
print(acc/len(Y_test))
由于训练集不够,还有算法本身限制,模型的准确率会随着初始化权重的变化而波动,有时候会达到1,有时候却很小,说明模型是欠拟合的,可以增加数据集或对特征进行升维.
机器学习作为AI的入门,学好机器学习对于后面学习深度学习和神经网络有很大帮助,比如说sigmoid函数,梯度下降方法等都会运用在深度学习算法中。