注:构建的是二元逻辑回归类,适用于二维数据的线性分类
In [1]:
import numpy as np
from math import sqrt
class LogisticRegression:
def __init__(self):
"""初始化logisticRegression模型"""
self.coef_=None#系数
self.intercept=None#系数
self._theta=None# θ[θ0,θ1,θ2]参数
def _sigmoid(self,t):
return 1./(1.+np.exp(-t))#Sigmoid函数的输出表征了当前样本标签为1的概率;
def fit(self ,X_train,y_train,eta=0.01,n_iters=1e4):#eta学习率,1e4是指1*10的4次方;
assert X_train.shape[0]==y_train.shape[0]#assert函数可以在条件不满足程序运行的情况下直接返回错误,而
不必等待程序运行后出现崩溃的情况。
def J(theta,X_b,y):#定义交叉熵损失函数,theta是θ参数,也是最后要优化的参数,X_b由多行[1,X1,X2]组成,y是实际值;
y_hat=self._sigmoid(X_b.dot(theta))#首先X_b.dot(θ),然后sigmoid,得到线性判断边界即:
hθ(x)=sigmoid(θ0+θ1X1+θ2X2),其中hθ(x)就是预测值y_hat
try :
return -np.sum(y*np.log(y_hat)+(1-y)*np.log(1-y_hat))/len(y)#这里交叉熵没有添加正则化项
except:
return float("inf")#返回无穷
def dJ(theta,X_b,y):#根据交叉熵损失函数,对θ求导
return X_b.T.dot(self._sigmoid(X_b.dot(theta))-y)/len(y)
def gradient_descent(X_b,y,initial_theta,eta,n_iters=1e4,epsilon=1e-8): #梯度下降
"""四个超参数:eta学习率,theta_initial是θ初始值,epsilon差距判断值是为了判断梯度是否为0(梯度不可能为0或者说很难找到,所以用epsilon=1e-8表示交叉熵值变化幅度替代梯度为0),n_iters迭代下降次数"""
theta=initial_theta
cur_iter = 0#cur_iter用来计量迭代次数
while cur_iter < n_iters: #开始迭代
gradient = dJ(theta, X_b, y) #梯度等于交叉熵求导值
last_theta = theta #上一个参数θ值
theta = theta - eta * gradient#更换参数
if (abs(J(theta, X_b, y) - J(last_theta, X_b, y)) < epsilon):#如果交叉熵变化幅度足够小于epsilon,就得到最优的参数θ,停止循环。
break
cur_iter += 1#迭代次数加1
return theta#返回的θ,就是需要求的最优参数
X_b = np.hstack([np.ones((len(X_train), 1)), X_train])# np.hstack()进行的是列操作,将行数与X相同的一维全是1的数组与X矩阵进行列拼接,即:[1i,X1i,X2i]。
initial_theta = np.zeros(X_b.shape[1])#初始化θ,构造出一组长度与x_b行数一样,全是零的数组
self._theta = gradient_descent(X_b, y_train, initial_theta, eta, n_iters) #调用gradient_descent梯度下降函数
self.intercept_ = self._theta[0]#得到θ0
self.coef_ = self._theta[1:]#得到θ1和θ2
return self#返回的是它自己,可以连续调用它的方法
def predict_proba(self, X_predict):
"""给定待预测数据集X_predict,返回表示X_predict的结果概率向量"""
assert self.intercept_ is not None and self.coef_ is not None#θ0、θ1、θ2不为空;
assert X_predict.shape[1] == len(self.coef_)#输入的X矩阵列数要与参数长度相同
X_b = np.hstack([np.ones((len(X_predict), 1)), X_predict])
return self._sigmoid(X_b.dot(self._theta)) #经过sigmoid函数,输出的就是(0,1)之间的概率
def predict(self, X_predict):
"""给定待预测数据集X_predict,返回表示X_predict的结果向量"""
assert self.intercept_ is not None and self.coef_ is not None#θ0、θ1、θ2不为空;
assert X_predict.shape[1] == len(self.coef_)#输入的X矩阵列数要与参数长度相同
proba = self.predict_proba(X_predict) #调用predict_proba()函数
return np.array(proba >= 0.5, dtype='int')#大于0.5就是1,反之是0,相当于分成0,1两类
def accuracy_score(self, X,y):
"""根据待测数据集 X 、y和y_predict确定当前模型的准确度"""
y_predict = self.predict(X) #预测出y_predict
assert len(y) == len(y_predict) #判断正确标签y与预测标签y_predict是否相同
score=np.sum(y_test == y_predict) / len(y) #计算准确率
return score#返回准确率
def __repr__(self): #返回显示类的自定义属性信息
return "LogisticRegression()"
2.实现逻辑回归
2.1处理数据集
In [2]:
import matplotlib.pyplot as plt
from sklearn import datasets
iris = datasets.load_iris()#iris数据集是用来给花做分类的数据集,iris包含150个样本,对应着数据集的每行数据;
#每行数据包含每个样本的四个特征和样本的类别信息。
X = iris.data#X是(150,4)矩阵
y = iris.target#y是(150,)矩阵,其中值只有0,1,2;iris数据集是对三种花分类,所以有三种标签就可以。
"""******************这里用iris数据集,主要是利用数据集的数据,拼凑出符合实验的数据,个人认为这并不具有具体意义,但强求的说(通过两种特征进行预测标签是0或1的花品种)****************"""
X = X[y<2,:2] #计算出X中符合y<2条件的数量为100,则将X矩阵中满足条件的100行提取,然后切前两列的数据。
y = y[y<2]#计算出在y中符合y<2的数量,为100;使y与X数据保持同步。
plt.scatter(X[y==0,0],X[y==0,1],color="r")#横坐标:X[y==0,0]的行满足y=0条件,列是X第一列的所有数据Xi,
#纵坐标:X[y==0,1]的行满足y=0条件,列是X第二列的所有数据y,将其得到的(Xi,y)点归为一类
plt.scatter(X[y==1,0],X[y==1,1],color="g")#横坐标:X[y==1,0]的行满足y=1条件,列是X的第一列的所有数据Xi,
#纵坐标:X[y==1,1]的行满足y=1条件,列是X第二列的所有数据y,将其得到的(Xi,y)点归为另一类