一、Sigmoid/Logistic函数:
我们定义逻辑回归的预测函数为ℎ() = () ,
其中g(x)函数是sigmoid函数。
注意此处,x都是列向量;
二、决策边界:
1、 = [-3; 1; 1] x = [0; x1; x2]
2、 = [-1; 0; 0; 1; 1] x = [0; x1; x2; x12; x22]
3、这是更复杂的逻辑边界:
三、逻辑回归的代价函数:
1、其中y代表真实值,ℎ()代表预测值;该代价函数也是一个凸函数,故而可以利用梯度下降法求解全局最小值
或者简化为如下形式:
(ℎ(), ) = −(ℎ()) − (1−)(1−ℎ())
2、代价函数的导数:
3、梯度下降法的伪代码描述:
四、多分类问题:
通过划定不止一个决策边界:
五、正确率/召回率/F1指标:
1、定义:
正确率:就是检索出来的条目有多少是正确的;
召回率:就是所有正确的条目有多少被检索出来了。
F1值=2∗(正确率∗召回率)/(正确率+召回率)。 是综合上面二个指标的评估指标。
这几个指标的取值都在0-1之间,数值越接近于1,效果越好。
2、分析:
我们希望检索结果Precision越高越好,同时Recall也越高越好,但事实上这两者在某些情况下有矛盾的。
比如极端情况下,我们只搜索出了一个结果,且是准确的,那么Precision就是100%,但是Recall就很低;
而如果我们把所有结果都返回,那么比如Recall是100%,但是Precision就会很低。
六、代码展示:
相关代码
my_first_logistic_linear.py:
import matplotlib.pyplot as plt
import numpy as np
#用来评估模型性能
from sklearn.metrics import classification_report
from sklearn import preprocessing
#是否对数据进行标准化处理
scale = True
#载入数据:
data = np.genfromtxt("LR-testSet.csv",delimiter=",")
x_data = data[:,:-1]
#给x_data添加偏置项,即线性逻辑回归中的截距
#np.concatenate(a,b)的功能是对矩阵进行扩充
#axis=0是沿Y轴进行扩充,b放置在A的下面
#axis=1是沿X轴进行扩充
x_data = np.concatenate((np.ones((100,1)), x_data), axis=1)
#赋予y_data的Y轴多一个维度,方便接下来的np.mat命令:
y_data = data[:,-1, np.newaxis]
#定义sigmod函数
def sigmod(x):
return 1.0/(1+np.exp(-x))
#定义代价函数
#这里Mat的意思是都已经是np中的矩阵模式
def cost(xMat, yMat, theta):
#np.multiply(a,b)会对这两个数组的对应元素进行计算,因此它要求这两个数组有相同的大小(shape相同),并返回一个新的numpy数组
#如果shape不同的话,会将小规格的矩阵延展成与另一矩阵一样大小,再求两者内积(不建议这样用)
#此处要求theta是一个列向量,保证xMat*theta是一个单值
left = np.multiply(yMat, np.log(sigmod(xMat*theta)))
right = np.multiply(1-yMat, np.log(1-sigmod(xMat*theta)))
#sum()中axis默认为0,即沿Y轴方向求和;
#len()中axis默认也为0,即沿Y轴方向求长度;
return np.sum(left + right) / -(len(xMat))
#梯度下降法
def gradAscent(xArr, yArr):
#根据scale的值决定是否进行正则化处理
if scale == True:
#sklearn.preprocessing.scale的作用是数据标准化;
#公式:(X-X_mean)/X_std 最后,对每个属性/每列来说所有数据都聚集在0附近,方差值为1
#参数说明:
#axis=0,表示沿Y轴处理;axis=1,表示沿X轴处理;
#with_mean=True,表示使均值为0;
#with_std=True,表示使方差为1;
xArr = preprocessing.scale(xArr, axis=0)
#np.mat(a)的作用是将a转换为矩阵
xMat = np.mat(xArr)
yMat = np.mat(yArr)
lr = 0.001 #学习度
epochs = 10000 #学习次数
#用来存储每迭代50次后的代价函数的值
costList = []
#行m代表数据个数;列n代表权值个数
#np.shape(a)会返回矩阵a的行数和列数
m,n = np.shape(xMat)
#初始化theta,显然theta需要是一个列向量
theta = np.mat(np.ones((n, 1)))
for i in range(epochs+1):
#初始化ℎ()
h = sigmod(xMat*theta)
#计算误差,注:此处直接对所有的j进行了更新,比伪代码中的描述更优越
#xMat.T的意思是对xMat进行转置
temp = xMat.T * (h-yMat) / m
theta = theta - lr * temp
if i%50 == 0:
costList.append(cost(xMat, yMat, theta))
return theta, costList
#训练模型
#补充,其实这里x_data和y_data已经是矩阵的形式了,所以gradAscent不适用np.mat也可以
theta, costList = gradAscent(x_data, y_data)
print(theta)
#绘图:
if scale == False:
x_data = data[:,:-1]
y_data = data[:,-1]
x0 = []
x1 = []
y0 = []
y1 = []
#切分不同类型的数据:
for i in range(len(x_data)):
if y_data[i]==0:
x0.append(x_data[i,0])
y0.append(x_data[i,1])
else:
x1.append(x_data[i,0])
y1.append(x_data[i,1])
#画散点图:
scatter0 = plt.scatter(x0, y0, c="b", marker="o")
scatter1 = plt.scatter(x1, y1, c="r", marker="x")
plt.legend(handles=[scatter0, scatter1], labels=["label0", "label1"], loc="best")
#画斜线:
#theta[0] + theta[1]*x1 + theta[2]*x2 = 0
x1 = np.mat([[-4], [3]])
x2 = (-theta[0] - x1*theta[1]) / theta[2]
plt.plot(x1, x2, "k")
plt.show()
else:
x_data = data[:,:-1]
y_data = data[:,-1]
x_data = preprocessing.scale(x_data, axis=0)
x0 = []
x1 = []
y0 = []
y1 = []
#切分不同类型的数据:
for i in range(len(x_data)):
if y_data[i]==0:
x0.append(x_data[i,0])
y0.append(x_data[i,1])
else:
x1.append(x_data[i,0])
y1.append(x_data[i,1])
#画散点图:
scatter0 = plt.scatter(x0, y0, c="b", marker="o")
scatter1 = plt.scatter(x1, y1, c="r", marker="x")
plt.legend(handles=[scatter0, scatter1], labels=["label0", "label1"], loc="best")
#画斜线:
x1 = np.mat([[-4], [3]])
x2 = (-theta[0] - x1*theta[1]) / theta[2]
plt.plot(x1, x2, "k")
plt.show()
#画代价函数的迭代情况:
#201表示一共有201个点:0、50、...、10000
x = np.linspace(0, 10001, 201)
plt.plot(x, costList, c="r")
plt.title("Train")
plt.xlabel("Epochs")
plt.ylabel("Cost")
plt.show()
#预测:
x_data = data[:,:-1]
x_data = np.concatenate((np.ones((100,1)), x_data), axis=1)
def predict(x_data, theta):
if scale == True:
x_data = preprocessing.scale(x_data, axis=0)
xMat = np.mat(x_data)
theta = np.mat(theta)
#sigmod(xMat*theta)是一个列向量;若其元素>0.5,返回1;否则返回0
return [1 if x>=0.5 else 0 for x in sigmod(xMat*theta)]
predictions = predict(x_data, theta)
print(classification_report(y_data, predictions))
用户流失预测.py:
import numpy as np
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import classification_report
from sklearn.linear_model import LogisticRegression
#导入测试集和训练集
#分隔符是“,”;结果数组类型是str
train_data = np.genfromtxt("Churn-Modelling.csv",delimiter=",",dtype=str)
test_data = np.genfromtxt("Churn-Modelling-Test-Data.csv",delimiter=",",dtype=str)
x_train = train_data[1:,:-1]
y_train = train_data[1:,-1]
x_test = train_data[1:,:-1]
y_test = train_data[1:,-1]
#前三列的数据无意义,因此删除
#np.delete(arr,obj,axis=None)
#arr:输入矩阵
#obj:需要处理的位置
#axis=0,在Y轴上选择删除;axis=1,在X轴上选择删除
x_train = np.delete(x_train, [0, 1, 2], axis=1)
x_test = np.delete(x_test, [0, 1, 2], axis=1)
#由于性别、国籍非数字类型,因此需要转化成数字类型
#LabelEncoder()的作用是把n个类别编码为0~n-1之间的整数,建立起1-1映射
#使用fit函数可以进行编码
#使用transform和invers_transform进行类别和整数之间的转换
#Ex:
#labelencoder1.inverse_transform([0,1,2])
#labelencoder1.transform(["France", "Germany", "Spain"])
labelencoder1 = LabelEncoder()
x_train[:,1] = labelencoder1.fit_transform(x_train[:,1])
x_test[:,1] = labelencoder1.fit_transform(x_test[:,1])
labelencoder2 = LabelEncoder()
x_train[:,2] = labelencoder1.fit_transform(x_train[:,2])
x_test[:,2] = labelencoder1.fit_transform(x_test[:,2])
#将引入的字符串类型数据转换为数字类型
#将str数组转换为float32数组
x_train = x_train.astype(np.float32)
x_test = x_test.astype(np.float32)
y_train = y_train.astype(np.float32)
y_test = y_test.astype(np.float32)
#归一化处理
#StandardScaler()的作用是数据标准化,公式:(X-X_mean)/X_std
#sklearn.preprocessing.scale(axis=0,with_mean=True,with_std=True)也有完全相同的作用
sc = StandardScaler()
x_train = sc.fit_transform(x_train)
x_test = sc.fit_transform(x_test)
#生成训练模型
LR = LogisticRegression()
LR.fit(x_train,y_train)
#对测试集进行预测
predictions = LR.predict(x_test)
print(classification_report(y_test, predictions))