【手搓深度学习算法】用逻辑回归分类双月牙数据集-非线性数据篇

用逻辑回归分类-非线性数据篇

前言

逻辑斯蒂回归是一种广泛使用的分类方法,它是基于条件概率密度函数的最大似然估计的。它的主要思想是将输入空间划分为多个子空间,每个子空间对应一个类别。在每个子空间内部,我们假设输入变量的取值与类别标签的概率成正比。

在逻辑斯蒂回归中,我们首先通过数据进行线性回归,得到的结果再通过sigmoid函数转化为概率,这样就可以得到每个类别的概率。然后,我们可以通过设置一个阈值,如果概率大于阈值,我们就认为这个样本属于这个类别,否则就属于其他类别。这就是逻辑斯蒂回归的基本原理。

逻辑斯蒂回归在现实生活中有很多应用,比如垃圾邮件分类、疾病诊断等。它可以处理非线性关系,而且它的预测结果是概率,这对于处理分类问题非常有用。

在深度学习中,逻辑斯蒂回归的作用主要体现在两个方面:一是作为一种基础的分类方法,它可以用于二分类问题,比如判断一个邮件是否为垃圾邮件;二是作为一种特征提取方法,它可以用于提取输入数据的特征,这些特征可以被其他深度学习模型使用。

本文重点总结逻辑斯蒂回归在非线性数据集上的应用方法,对线性数据分类还有疑惑的跳转
逻辑斯蒂回归多分类-线性数据篇

名词解释

回归

很多初学(复习)统计学或者深度学习算法的同学(包括我),对“回归”这个名词可能感觉有点疑惑,因为它既熟悉又陌生,熟悉是因为它在现实生活中也很常见,比如香港回归,澳门回归。。。,陌生的是当它跟统计学名词联系在一起,又会让人有点摸不着头脑,什么线性回归,逻辑斯蒂回归。。。,为此,我专门查找了相关资料,总结如下:

在统计学和深度学习中,“回归”这个术语的含义主要是关于预测一个连续的目标变量。这个目标变量可以是任何可以连续变化的东西,比如销售额、房价、股票价格等。在这种情况下,“回归”的意思是“倒推”或者“预测”。

在统计学中,我们使用回归分析来研究一个或多个自变量(即影响因素)与一个因变量(即我们想要预测的结果)之间的关系。例如,我们可能会使用回归分析来研究房价与房屋面积、位置、年份等因素的关系。在这种情况下,我们的目标是找到一个函数,这个函数可以根据这些因素预测房价。这就是“回归”的含义:我们是在“倒推”或者“预测”房价。

在深度学习中,我们也使用回归模型,但是这里的“回归”更多的是指预测一个连续的目标变量。例如,我们可能会使用深度学习的回归模型来预测一个物品的评分,或者预测一个人的年龄。在这种情况下,我们的目标是找到一个函数,这个函数可以根据一些输入特征预测这个连续的目标变量。这也是“回归”的含义:我们是在“倒推”或者“预测”这个连续的目标变量。

总的来说,无论是在统计学还是在深度学习中,“回归”的含义都是“倒推”或者“预测”一个连续的目标变量。这个目标变量可以是任何可以连续变化的东西,比如销售额、房价、股票价格、评分、年龄等。我们的目标是找到一个函数,这个函数可以根据一些输入特征预测这个连续的目标变量。

使用逻辑斯蒂回归对线性数据和非线性数据进行训练和预测有什么异同

请添加图片描述
逻辑斯蒂回归是一种广义的线性模型,可以用于二分类问题。它本质上是线性回归模型,但在输出层使用了sigmoid函数,将线性回归的输出映射到(0,1)区间,表示概率。

在处理线性数据时,逻辑斯蒂回归的表现与普通线性回归类似。训练过程主要是通过最小化预测概率与实际标签之间的损失函数来进行的。在预测阶段,给定新的输入数据,模型会输出每个类别的概率。

然而,当处理非线性数据时,逻辑斯蒂回归仍然保持其线性特性。这并不意味着它不能处理非线性问题,而是说它通过引入非线性映射函数(如sigmoid函数)来处理数据内在的非线性关系。这种处理方式允许逻辑斯蒂回归在非线性数据上表现出色,而无需改变其作为线性回归模型的内在机制。

总结来说,逻辑斯蒂回归在处理非线性数据时,其训练和预测过程与处理线性数据时的主要区别在于如何解释和使用模型的输出。在任何情况下,它都保持了其作为线性模型的特性,只是在更高层次上(即通过sigmoid函数)引入了非线性。

虽然逻辑回归本质可以处理非线性数据,但它本质上还是一个线性模型,很多情况下对非线性数据不能很好的拟合,我们可以运用特征工程对输入数据集进行处理,本文通过引入多项式变换进行试验,比较引入前后的准确率和决策边界。

对输入数据进行多项式变换在逻辑斯蒂回归中主要有两个作用:

  • 处理非线性问题:当数据内在存在非线性关系时,通过多项式变换可以将这些非线性关系转换为线性关系,使得逻辑斯蒂回归能够更好地拟合数据。
  • 增加模型的灵活性:多项式变换允许模型捕捉到更复杂的输入和输出之间的关系,从而提高了模型的预测能力。 *

实现

工具函数

Sigmoid

Sigmoid函数是一种常用的激活函数,它将任意实数映射到0和1之间。Sigmoid函数的定义如下:

σ ( x ) = 1 1 + e − x \sigma(x) = \frac{1}{1 + e^{-x}} σ(x)=1+ex1

其中, x x x是输入, σ ( x ) \sigma(x) σ(x)是输出。

Sigmoid函数的图像如下(不平滑因为是我自己生成的。。。):
在这里插入图片描述

Sigmoid函数的主要特性包括:

  1. 单调递增:对于所有的 x x x σ ( x ) \sigma(x) σ(x)都是单调递增的。

  2. 输出范围在0和1之间:对于所有的 x x x 0 ≤ σ ( x ) ≤ 1 0 \leq \sigma(x) \leq 1 0σ(x)1

  3. 可微:Sigmoid函数是可微的,这使得它可以用于神经网络的反向传播算法。

def sigmoid(data):return 1 / (1+np.exp(-data))
数据预处理函数

对数据进行标准化和多项式变换,本文将分别比较“不使用多项式变换”、“不同角度的多项式变换”对准确率和拟合、泛化的影响

def prepare_data(data, normalize_data=False, polynomial_transform_degree=-1): assert isinstance(data, np.ndarray), "Data must be a numpy array" # 标准化特征矩阵  if normalize_data:  features_mean = np.mean(data, axis=0)  #特征的平均值features_dev = np.std(data, axis=0)    #特征的标准偏差features = (data - features_mean) / features_dev  #标准化数据else:  features_mean = None  features_dev = None  features = data# 多项式特征变换if (polynomial_transform_degree != -1):degree = polynomial_transform_degreenew_features = []for i in range(degree + 1):new_features.append(np.power(features, i))features = np.column_stack(new_features)data_processed = features# 返回处理后的数据return data_processed, features_mean, features_dev
非线性数据模拟生成函数

定义了一个名为moon2Data的函数,用于生成模拟数据,这些数据类似于月亮的形状
请添加图片描述

函数名为moon2Data,它接受两个参数:datanumshowdatanum表示要生成的点的数量,show是一个布尔值,决定是否显示生成的点。函数的返回类型是np.ndarray,即NumPy数组。

生成月亮形状的第一部分(代表类0的数据)。

x1 = np.linspace(-3, 3, datanum)
noise = np.random.randn(datanum) * 0.15
y1 = -np.square(x1) / 3 + 4.5 + noise

x1是在-3到3之间的均匀分布的datanum`个值。然后添加了高斯噪声。对于y1,它是x1的负平方除以3再加4.5后再加上噪声。

这部分代码将x1和y1重新整形为列向量,并添加一个全为0的列,用于代表分类标签(这里使用0表示类0)。

class0 = np.concatenate((x1.reshape(datanum,1), y1.reshape(datanum,1), np.zeros((datanum, 1))), axis=1)

生成月亮形状的第二部分(代表类1的数据)。

x2 = np.linspace(0, 6, datanum)
noise = np.random.randn(datanum) * 0.15
y2 = np.square(x2 - 3) / 3 + 0.5 + noise

x2是在0到6之间的均匀分布的datanum`个值。然后添加了高斯噪声。对于y2,它是x2减去3后的平方除以3再加0.5后再加上噪声。
与生成类0的数据类似,但是添加了一个全为1的列作为分类标签。

class1 = np.concatenate((x2.reshape(datanum,1), y2.reshape(datanum,1), np.ones((datanum, 1))), axis=1)

将类0和类1的数据合并为一个大的NumPy数组。

ret = np.concatenate((class0, class1), axis=0)

如果show参数为True,则使用matplotlib显示生成的点。这主要用于可视化目的。

if (show):plt.clf()  # 清除当前图形plt.axis([-3.5, 6.5, -.5, 5.5])  # 设置轴的范围plt.scatter(x1, y1, s=10)  # 绘制类0的点plt.scatter(x2, y2, s=10)  # 绘制类1的点plt.draw()  # 更新图形显示plt.pause(.1)  # 暂停0.1秒,以便查看图形plt.show()  # 显示图形

最后,函数返回合并后的数据。

return ret
def moon2Data(datanum, show = False) -> np.ndarray:x1 = np.linspace(-3, 3, datanum)noise = np.random.randn(datanum) * 0.15y1 = -np.square(x1) / 3 + 4.5 + noiseclass0 = np.concatenate((x1.reshape(datanum,1), y1.reshape(datanum,1), np.zeros((datanum, 1))), axis=1)x2 = np.linspace(0, 6, datanum)noise = np.random.randn(datanum) * 0.15y2 = np.square(x2 - 3) / 3 + 0.5 + noiseclass1 = np.concatenate((x2.reshape(datanum,1), y2.reshape(datanum,1), np.ones((datanum, 1))), axis=1)ret = np.concatenate((class0, class1), axis=0)if (show):plt.clf()plt.axis([-3.5, 6.5, -.5, 5.5])plt.scatter(x1, y1, s=10)plt.scatter(x2, y2, s=10)plt.draw()plt.pause(.1)plt.show()return ret

逻辑斯蒂回归类

以下代码在名为"LogisticRegression"的类中,大部分代码都和线性篇里面介绍的相同

初始化

将传入的数据集和标签记录下来以便进一步处理,同时初始化权重矩阵

def __init__(self, data,labels, normalize = False, polynomial_transform_degree = -1) -> None:self.normalize = normalizeself.polynomial_transform_degree = polynomial_transform_degreedata_processed, mean, dev = prepare_data(data, normalize, polynomial_transform_degree)self.data = data_processedself.labels = labelsself.unique_labels = np.unique(labels) #取标签中的类的名称的集合self.num_examples = self.data.shape[0] #取数据集中样本的数量self.num_features = self.data.shape[1] #取数据集中的特征个数num_unique_labels = self.unique_labels.shape[0] #取标签中类的个数self.theta = np.zeros((num_unique_labels,self.num_features)) #初始化权重向量,因为我们是多分类,所以权重是 “分类数量 x 特征个数”的矩阵

训练函数

对每个可能的分类执行训练过程,因为逻辑斯蒂回归通常用于处理二分类问题,所以每次都将当前分类的标签置为“1”,其他分类的标签置为“0”

def train(self, lr = 0.01, max_iter = 1000):cost_histories = [] #记录损失历史记录,以便可视化for label_index, unique_label in enumerate(self.unique_labels): #对所有可能的分类进行遍历current_initial_theta = np.copy(self.theta[label_index].reshape(self.num_features, 1)) #复制对应的权重向量,以避免直接修改current_labels = (self.labels == unique_label).astype(float) #等于当前标签的置为1,否则为0(current_theta, cost_history) = LogisticRegression.gradient_descent(self.data, current_labels, current_initial_theta, max_iter) #执行梯度下降过程self.theta[label_index] = current_theta.T #记录当前分类最终的权重向量cost_histories.append(cost_history) #记录当前分类最终的损失历史记录return cost_histories, self.theta

梯度下降过程

在规定的迭代次数里执行梯度下降,每次权重向量都减去学习率乘以当前梯度,通过迭代优化模型的参数(也就是权重向量 θ \theta θ),使得模型的预测结果与真实标签之间的差异(也就是损失函数)最小。

gradient_descent 方法的主要任务是执行梯度下降的迭代过程。它接收五个参数:数据集 data、标签 labels、当前的初始权重向量 current_initial_theta、学习率 lr 和最大迭代次数 max_iter。在每次迭代中,它会调用 gradient_step 方法计算梯度,然后根据梯度更新权重向量。同时,它还会记录每次迭代后的损失值,以便于后续的可视化或者调试。

gradient_step 方法的主要任务是计算梯度。它接收三个参数:数据集 data、标签 labels 和当前的权重向量 theta。首先,它会计算预测值 predictions,然后计算预测值与真实标签之间的差异 label_diff。最后,它会计算梯度 gradients,并返回梯度的平均值。

在这里,梯度的计算公式为:

∇ θ J ( θ ) = 1 N ∑ i = 1 N ( h θ ( x ( i ) ) − y ( i ) ) x ( i ) \nabla_\theta J(\theta) = \frac{1}{N} \sum_{i=1}^{N} (h_\theta(x^{(i)}) - y^{(i)})x^{(i)} θJ(θ)=N1i=1N(hθ(x(i))y(i))x(i)

其中, N N N 是数据集的大小, h θ ( x ( i ) ) h_\theta(x^{(i)}) hθ(x(i)) 是预测值, y ( i ) y^{(i)} y(i) 是真实标签, x ( i ) x^{(i)} x(i) 是第 i i i 个数据点。

这个公式的含义是,对于每一个数据点,我们都计算了预测值与真实标签之间的差异,然后乘以该数据点的特征向量,最后将所有数据点的结果加起来,再除以数据集的大小,得到的就是梯度。

这个梯度反映了模型预测结果与真实标签之间的差异,我们希望通过不断地调整权重向量,使得这个差异越来越小,从而提高模型的预测效果。

def gradient_descent(data, labels, current_initial_theta, lr, max_iter):cost_history = [] #损失历史num_features = data.shape[1] #特征数量optimized_theta = current_initial_theta #取初始权重for _ in range(max_iter): #在规定的迭代次数范围内迭代optimized_theta -= float(lr) * LogisticRegression.gradient_step(data, labels, optimized_theta).reshape(num_features, 1) #执行单步更新权重cost_history.append(LogisticRegression.cost_function(data, labels, optimized_theta)) # 记录损失历史optimized_theta = optimized_theta.reshape(num_features, 1)return optimized_theta, cost_historydef gradient_step(data, labels, theta):num_examples = labels.shape[0] #样本数量predictions = LogisticRegression.predict(data.T, theta) #特征和权重的点积然后执行sigmoid,得到概率label_diff = predictions - labels #差异gradients = (1/num_examples)*np.dot(data.T, label_diff) #计算梯度,差异和数据的点积除以样本数量return gradients.T.flatten() #将梯度转换为一维

损失函数

主要功能是计算模型的预测结果与真实标签之间的差异,也就是损失函数。

cost_function 方法的主要任务是计算损失函数。它接收三个参数:数据集 data、标签 labels 和当前的权重向量 theta
首先,它会计算预测值 prediction
然后根据预测值和真实标签的关系,计算两部分的损失 y_is_set_costy_is_not_set_cost
最后,它会将这两部分的损失相加,然后除以数据集的大小,得到的就是损失函数。

在这里,损失函数的计算公式为:

J ( θ ) = − 1 N [ ∑ i = 1 N y ( i ) log ⁡ ( h θ ( x ( i ) ) ) + ( 1 − y ( i ) ) log ⁡ ( 1 − h θ ( x ( i ) ) ) ] J(\theta) = -\frac{1}{N}\left[\sum_{i=1}^{N}y^{(i)}\log(h_\theta(x^{(i)})) + (1 - y^{(i)})\log(1 - h_\theta(x^{(i)}))\right] J(θ)=N1[i=1Ny(i)log(hθ(x(i)))+(1y(i))log(1hθ(x(i)))]

其中, N N N 是数据集的大小, y ( i ) y^{(i)} y(i) 是第 i i i 个数据点的真实标签, h θ ( x ( i ) ) h_\theta(x^{(i)}) hθ(x(i)) 是第 i i i 个数据点的预测值。

这个公式的含义是,对于每一个数据点,如果它的真实标签是1,那么我们就计算 log ⁡ ( h θ ( x ( i ) ) ) \log(h_\theta(x^{(i)})) log(hθ(x(i))) 的值;如果它的真实标签是0,因为 h θ ( x ( i ) ) h_\theta(x^{(i)}) hθ(x(i)) 计算的是 p r e d i c t = 1 predict = 1 predict=1的概率,所以我们就计算 log ⁡ ( 1 − h θ ( x ( i ) ) ) \log(1 - h_\theta(x^{(i)})) log(1hθ(x(i))) 的值。然后,我们将所有数据点的结果加起来,再除以数据集的大小,得到的就是损失函数。

损失函数反映了模型预测结果与真实标签之间的差异,我们希望通过不断地调整权重向量,使得这个差异越来越小,从而提高模型的预测效果。


def cost_function(data, labels, theta):num_examples = data.shape[0] #样本数量prediction = LogisticRegression.predict(data.T, theta) #特征和权重的点积然后执行sigmoid,得到概率y_is_set_cost = np.dot(labels[labels == 1].T, np.log(prediction[labels == 1])) #当标签等于1时的权重向量y_is_not_set_cost = np.dot(1-labels[labels == 0].T, np.log(1-prediction[labels == 0])) #当标签等于0时的权重向量cost = (-1/num_examples) * (y_is_set_cost+y_is_not_set_cost) # 计算整体损失return cost

预测计算函数

特征和权重的点积然后计算Sigmoid得到概率


def predict(data, theta):predictions = sigmoid(np.dot(data.T, theta))return predictions

验证计算函数

验证计算函数先对输入特征矩阵进行预测计算,然后对每个样本,计算其概率最大值,最后将每个样本的最佳预测分类和每个样本属于三个分类的概率返回。

def predict_test(self, data):num_examples = data.shape[0] #样本数量prob = LogisticRegression.predict(data.T, self.theta.T) #计算预测值max_prob_index = np.argmax(prob, axis = 1) #最大的概率值的indexclass_prediction = np.empty(max_prob_index.shape, dtype=object) # 初始化预测结果for index,label in enumerate(self.unique_labels):class_prediction[max_prob_index == index] = label # 取预测值return class_prediction.reshape((num_examples,1)), prob

完整流程

  1. 数据集的准备和分割:

    • 首先调用 moon2Data 函数生成一个非线性可分的数据集
    • 数据集被随机打乱,然后分为训练集和测试集,比例为 80% 训练数据和 20% 测试数据。
  2. 逻辑回归模型的初始化:

    • 创建 LogisticRegression 类的实例 logistic_reg,传入训练数据、训练标签,并设置数据标准化和多项式特征转换的参数。这些步骤对数据进行预处理,以提高模型的性能。
  3. 模型训练:

    • 调用 logistic_reg 实例的 train 方法,传入学习率和最大迭代次数,开始训练过程。训练过程中,模型的参数(权重)会通过梯度下降算法进行优化,以最小化损失函数。
  4. 损失历史的可视化:

    • 使用 matplotlib 库绘制损失历史曲线,这有助于观察模型在训练过程中损失的变化情况。
  5. 模型测试和预测信息的显示:

    • 使用训练好的模型对测试数据进行预测,并调用 show_predict_info 函数显示预测的准确率和错误预测的详细信息。
  6. 测试数据的可视化:

    • 根据测试数据的标签将数据分为两类,并使用散点图进行可视化,以展示数据的分布情况。
  7. 非线性决策边界的绘制:

    • 生成一个网格,覆盖当前图形的 x 和 y 轴范围。
    • 使用训练好的模型对这个网格上的每个点进行预测,以确定它们属于哪个类别。
    • 绘制等高线(决策边界),这有助于直观地理解模型是如何将不同类别的数据分开的。
  8. 结束:

    • 最后,再次使用训练数据进行预测,并显示预测信息,这有助于评估模型在训练集上的性能。

通过逻辑回归模型对数据进行分类,并通过可视化来评估模型的性能。

import pandas as pd
import matplotlib.pyplot as plt
def main():moon_shape_dataset = moon2Data(300)print (moon_shape_dataset.shape)np.random.shuffle(moon_shape_dataset)train_dataset = moon_shape_dataset[:int(len(moon_shape_dataset)*0.8):]test_dataset = moon_shape_dataset[int(len(moon_shape_dataset)*0.8):]train_labels = train_dataset[:,-1:]test_labels = test_dataset[:,-1:]train_data = train_dataset[:,:-1]test_data = test_dataset[:,:-1]logistic_reg = LogisticRegression(train_data, train_labels, normalize=True, polynomial_transform_degree=3)loss_history, theta = logistic_reg.train(0.001, 50000)plt.close()plt.plot(loss_history[0])plt.plot(loss_history[1])plt.show()prediction, prop = logistic_reg.predict_test(test_data)show_predict_info(prediction, prop, test_labels)class0_data = test_dataset[test_dataset[:,-1]==0.0]class1_data = test_dataset[test_dataset[:,-1]==1.0]plt.close()plt.scatter(class0_data[:,0], class0_data[:,1])plt.scatter(class1_data[:,0], class1_data[:,1]) # 绘制非线性决策边界  x_min, x_max = plt.xlim()  y_min, y_max = plt.ylim()  x_grid = np.linspace(x_min, x_max, 100)y_grid = np.linspace(y_min, y_max, 100)x,y = np.meshgrid(x_grid, y_grid)grid = np.concatenate((x.reshape(-1,1), y.reshape(-1,1)), axis=1)values, probs = logistic_reg.predict_test(grid)values = values.reshape(100,100).astype("float")# 绘制等高线plt.contour(x, y, values)# 添加标题和轴标签plt.title('Non Linear Plot')plt.xlabel('X position')plt.ylabel('Y position')# 显示图形plt.show()prediction, prop = logistic_reg.predict_test(train_data)show_predict_info(prediction, prop, train_labels)if (__name__ == "__main__"):main()

不使用多项式变换时的准确率和决策边界

我们通过传入“-1”告诉数据处理函数不进行多项式变换

    logistic_reg = LogisticRegression(train_data, train_labels,normalize=True, polynomial_transform_degree=-1)

可以看到虽然准确率还不错“Accuracy is [0.9]”,但是决策边界明显还是线性的,在训练集和测试集都没有做到拟合
在这里插入图片描述

使用多项式设置degree=2时的准确率和决策边界

我们通过传入“2”告诉数据处理函数进行多项式变换

    logistic_reg = LogisticRegression(train_data, train_labels,normalize=True, polynomial_transform_degree=2)

可以看到准确率和上面几乎相同,而且决策边界也没有拟合数据,这是因为:
当degree=2时,多项式变换会在原始特征的基础上添加它们的平方项和所有可能的两两特征组合(交互项)。这确实增加了模型的非线性能力,但是否足以捕捉到数据的非线性关系,以及决策边界的形状,取决于几个因素:

  1. 数据集的特征数:如果原始数据集只有一个特征,那么即使是二次多项式变换,也只会产生一个平方项,这在二维空间中仍然是一条抛物线,而不是复杂的非线性边界。如果有两个或更多的特征,二次多项式变换会产生交互项,这可能会导致非线性决策边界,但这仍然取决于数据的分布。

  2. 数据的分布:如果数据的真实分布是高度非线性的,那么即使添加了平方项和交互项,二次多项式变换可能仍然不足以捕捉到所有的非线性关系。在这种情况下,决策边界可能仍然看起来相对平滑和简单。

  3. 模型的限制:逻辑斯蒂回归是一个线性模型,它的决策边界是特征空间中的一个超平面。即使通过多项式变换增加了特征,逻辑斯蒂回归仍然试图找到一个超平面来分隔这些特征。
    在这里插入图片描述

使用多项式设置degree=5时的准确率和决策边界

我们通过传入“5”告诉数据处理函数进行多项式变换

    logistic_reg = LogisticRegression(train_data, train_labels,normalize=True, polynomial_transform_degree=5)

可以看到在测试数据集上变现除了良好的准确率Accuracy is [0.99166667],而且决策边界也充分拟合了数据
在这里插入图片描述

使用多项式设置degree=15时的准确率和决策边界

我们通过传入“15”告诉数据处理函数进行多项式变换

    logistic_reg = LogisticRegression(train_data, train_labels,normalize=True, polynomial_transform_degree=15)

可以看到测试集准确率为Accuracy is [0.95833333],已经出现了过拟合(虽然不明显,可见逻辑斯蒂回归还是很强大的…)
在这里插入图片描述

完整代码(数据集自动生成,不用下载)

import numpy as np    
from scipy.optimize import minimize
from 生成非线性数据集 import moon2Data
from sklearn.preprocessing import PolynomialFeaturesdef sigmoid(data):return 1 / (1+np.exp(-data))def prepare_data(data, normalize_data=False, polynomial_transform_degree=-1): assert isinstance(data, np.ndarray), "Data must be a numpy array" # 标准化特征矩阵  if normalize_data:  features_mean = np.mean(data, axis=0)  #特征的平均值features_dev = np.std(data, axis=0)    #特征的标准偏差features = (data - features_mean) / features_dev  #标准化数据else:  features_mean = None  features_dev = None  features = data# 多项式特征变换if (polynomial_transform_degree != -1):degree = polynomial_transform_degreenew_features = []for i in range(degree + 1):new_features.append(np.power(features, i))features = np.column_stack(new_features)data_processed = features# 返回处理后的数据return data_processed, features_mean, features_devdef show_predict_info(prediction, prop, labels):accuracy = sum(prediction == labels) / len(labels)print("Accuracy is {}".format(accuracy))fail_predict = []for index, item in enumerate(prediction.flatten()):if (item != labels.flatten()[index]):info = {}info["index"]=indexinfo["predict"]=iteminfo["actual"]=labels.flatten()[index]fail_predict.append(info)for predict_info in fail_predict:print("Predict is {} prop is {}, actual is {} prop is {}".format(predict_info["predict"], prop[predict_info["index"], [int(predict_info["predict"])]],predict_info["actual"], prop[predict_info["index"], [int(predict_info["actual"])]]))class LogisticRegression:'''1. 对数据进行预处理操作2. 先得到所有的特征个数3. 初始化参数矩阵'''def __init__(self, data,labels, normalize = False, polynomial_transform_degree = -1) -> None:self.normalize = normalizeself.polynomial_transform_degree = polynomial_transform_degreedata_processed, mean, dev = prepare_data(data, normalize, polynomial_transform_degree)self.data = data_processedself.labels = labelsself.unique_labels = np.unique(labels) #取标签中的类的名称的集合self.num_examples = self.data.shape[0] #取数据集中样本的数量self.num_features = self.data.shape[1] #取数据集中的特征个数num_unique_labels = self.unique_labels.shape[0] #取标签中类的个数self.theta = np.zeros((num_unique_labels,self.num_features)) #初始化权重向量,因为我们是多分类,所以权重是 “分类数量 x 特征个数”的矩阵def train(self, lr = 0.01, max_iter = 1000):cost_histories = [] #记录损失历史记录,以便可视化for label_index, unique_label in enumerate(self.unique_labels): #对所有可能的分类进行遍历current_initial_theta = np.copy(self.theta[label_index].reshape(self.num_features, 1)) #复制对应的权重向量,以避免直接修改current_labels = (self.labels == unique_label).astype(float) #等于当前标签的置为1,否则为0(current_theta, cost_history) = LogisticRegression.gradient_descent(self.data, current_labels, current_initial_theta, max_iter) #执行梯度下降过程#(current_theta, cost_history) = LogisticRegression.gradient_descent(self.data, current_labels, current_initial_theta, '''lr,''' max_iter) #执行梯度下降过程self.theta[label_index] = current_theta.T #记录当前分类最终的权重向量cost_histories.append(cost_history) #记录当前分类最终的损失历史记录return cost_histories, self.theta@staticmethoddef gradient_descent(data, labels, current_initial_theta, max_iter):cost_history = []num_features = data.shape[1]#scipy.optimize.minimize`函数被用于实现梯度下降算法。这个函数的主要作用是找到一个最小值点,使得目标函数的值最小。result = minimize(#要优化的目标fun = lambda current_theta:LogisticRegression.cost_function(data, labels, current_theta.reshape(num_features, 1)),#初始化的权重参数x0 = current_initial_theta.flatten(),#选择优化策略, 表示使用共轭梯度法进行优化method='CG',#梯度下降迭代计算公式jac= lambda current_theta:LogisticRegression.gradient_step(data, labels,current_theta.reshape(num_features, 1)),#记录结果callback = lambda current_theta:cost_history.append(LogisticRegression.cost_function(data, labels, current_theta.reshape(num_features, 1))),#迭代次数options = {'maxiter':max_iter})if not (result.success):raise ArithmeticError('Can not minimize cost function ' + result.message)#minimize`函数的输出是一个结果对象,其中`result.x`是找到的最小值点,`result.fun`是在最小值点处的目标函数值。optimized_theta = result.x.reshape(num_features, 1)return optimized_theta, cost_history'''@staticmethoddef gradient_descent(data, labels, current_initial_theta, lr, max_iter):cost_history = [] #损失历史num_features = data.shape[1] #特征数量optimized_theta = current_initial_theta #取初始权重for index, _ in enumerate(range(max_iter)): #在规定的迭代次数范围内迭代optimized_theta -= float(lr) * LogisticRegression.gradient_step(data, labels, optimized_theta).reshape(num_features, 1) #执行单步更新权重loss = LogisticRegression.cost_function(data, labels, optimized_theta)if (index % 10 == 0):print("Step {} loss is {}".format(index, loss))cost_history.append(loss) # 记录损失历史optimized_theta = optimized_theta.reshape(num_features, 1)return optimized_theta, cost_history'''@staticmethoddef gradient_step(data, labels, theta):num_examples = labels.shape[0] #样本数量predictions = LogisticRegression.predict(data.T, theta) #特征和权重的点积然后执行sigmoid,得到概率label_diff = predictions - labels #差异gradients = (1/num_examples)*np.dot(data.T, label_diff) #计算梯度,差异和数据的点积除以样本数量return gradients.T.flatten() #将梯度转换为一维@staticmethoddef cost_function(data, labels, theta):num_examples = data.shape[0] #样本数量prediction = LogisticRegression.predict(data.T, theta) #特征和权重的点积然后执行sigmoid,得到概率y_is_set_cost = np.dot(labels[labels == 1].T, np.log(prediction[labels == 1])) #当标签等于1时的权重向量y_is_not_set_cost = np.dot(1-labels[labels == 0].T, np.log(1-prediction[labels == 0])) #当标签等于0时的权重向量cost = (-1/num_examples) * (y_is_set_cost+y_is_not_set_cost) # 计算整体损失return cost@staticmethoddef predict(data, theta):predictions = sigmoid(np.dot(data.T, theta))return predictionsdef predict_test(self, data):data_processed = prepare_data(data, self.normalize, self.polynomial_transform_degree)[0]num_examples = data_processed.shape[0] #样本数量prob = LogisticRegression.predict(data_processed.T, self.theta.T) #计算预测值max_prob_index = np.argmax(prob, axis = 1) #最大的概率值的indexclass_prediction = np.empty(max_prob_index.shape, dtype=object) # 初始化预测结果for index,label in enumerate(self.unique_labels):class_prediction[max_prob_index == index] = label # 取预测值return class_prediction.reshape((num_examples,1)), probimport pandas as pd
import matplotlib.pyplot as plt
def main():'''iris_dataset = "J:\\MachineLearning\\数据集\\Iris\\iris.data"dataset_src = pd.read_csv(iris_dataset).valuesnp.random.shuffle(dataset_src)print("The shape of original dataset is {}".format(dataset_src.shape))train_data = dataset_src[:int(len(dataset_src)*0.8)]test_data = dataset_src[int(len(dataset_src)*0.8):]train_dataset = train_data[:, :-1].astype('float')train_label = train_data[:, -1].reshape(-1,1)test_dataset = test_data[:, :-1].astype('float')test_label = test_data[:, -1].reshape(-1,1)print("The shape of train dataset is {}".format(train_dataset.shape))print("The shape of test dataset is {}".format(test_dataset.shape))print("The shape of train label is {}".format(train_label.shape))print("The shape of test label is {}".format(test_label.shape))# 创建一个字典,将每种花的名称映射到一个唯一的整数flower_dict = {'Iris-versicolor': 0,'Iris-setosa': 1,'Iris-virginica': 2}# 使用字典来转换数组converted_array = [[flower_dict[item[0]]] for item in train_label]train_label = np.array(converted_array).reshape(-1,1).astype('int')converted_array = [[flower_dict[item[0]]] for item in test_label]test_label = np.array(converted_array).reshape(-1,1).astype('int')print(train_dataset)print(train_label)logistic_reg = LogisticRegression(np.array(train_dataset), np.array(train_label))(loss_history, theta) = logistic_reg.train(lr=0.001, max_iter= 10000)plt.plot(loss_history[0])plt.plot(loss_history[1])plt.plot(loss_history[2])prediction, prop = logistic_reg.predict_test(test_dataset)show_predict_info(prediction, prop, test_label)prediction, prop = logistic_reg.predict_test(train_dataset)show_predict_info(prediction, prop, train_label)'''moon_shape_dataset = moon2Data(300)print (moon_shape_dataset.shape)np.random.shuffle(moon_shape_dataset)train_dataset = moon_shape_dataset[:int(len(moon_shape_dataset)*0.8):]test_dataset = moon_shape_dataset[int(len(moon_shape_dataset)*0.8):]train_labels = train_dataset[:,-1:]test_labels = test_dataset[:,-1:]train_data = train_dataset[:,:-1]test_data = test_dataset[:,:-1]logistic_reg = LogisticRegression(train_data, train_labels, normalize=True, polynomial_transform_degree=15)loss_history, theta = logistic_reg.train(0.001, 50000)plt.close()plt.plot(loss_history[0])plt.plot(loss_history[1])plt.show()prediction, prop = logistic_reg.predict_test(test_data)show_predict_info(prediction, prop, test_labels)class0_data = test_dataset[test_dataset[:,-1]==0.0]class1_data = test_dataset[test_dataset[:,-1]==1.0]plt.close()plt.scatter(class0_data[:,0], class0_data[:,1])plt.scatter(class1_data[:,0], class1_data[:,1]) # 绘制非线性决策边界  x_min, x_max = plt.xlim()  y_min, y_max = plt.ylim()  x_grid = np.linspace(x_min, x_max, 100)y_grid = np.linspace(y_min, y_max, 100)x,y = np.meshgrid(x_grid, y_grid)grid = np.concatenate((x.reshape(-1,1), y.reshape(-1,1)), axis=1)values, probs = logistic_reg.predict_test(grid)values = values.reshape(100,100).astype("float")# 绘制等高线plt.contour(x, y, values)# 添加标题和轴标签plt.title('Non Linear Plot')plt.xlabel('X position')plt.ylabel('Y position')# 显示图形plt.show()prediction, prop = logistic_reg.predict_test(train_data)show_predict_info(prediction, prop, train_labels)if (__name__ == "__main__"):main()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/610329.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024年寒假即将来临 如何实现高质量亲子陪伴?

近日,各地陆续公布了寒假放假时间安排,孩子们期待着迎来一个休闲和轻松的假期。然而,对于许多家长来说,他们也意识到寒假不仅是一个放松的时间,更是一个增进亲子关系的宝贵机会,可以为孩子提供有意义的学习陪伴。 自“双减”政策落地后,学生的课业负担减轻许多,拥有了更多的空余…

【leetcode 2707. 字符串中的额外字符】动态规划 字典树

2707. 字符串中的额外字符 题目描述 给你一个下标从 0 开始的字符串 s 和一个单词字典 dictionary 。你需要将 s 分割成若干个 互不重叠 的子字符串,每个子字符串都在 dictionary 中出现过。s 中可能会有一些 额外的字符 不在任何子字符串中。 请你采取最优策略分割…

Go 如何处理死锁以提供哪些工具来检测或防死锁?

并发是 Go 的核心特性,它使程序能够同时处理多个任务。它是现代编程的一个强大组件,如果使用正确,可以产生高效、高性能的应用程序。然而,并发性也带来了顺序编程中不存在的某些类型错误的可能性,其中最臭名昭著的是死…

拼多多API的未来:无限可能性和创新空间

拼多多,作为中国电商市场的巨头之一,自成立以来一直保持着高速的发展态势。其API的开放为开发者提供了无限的可能性和创新空间,使得更多的商业逻辑和功能得以实现。本文将深入探讨拼多多API的未来发展,以及它所具备的无限可能性和…

面相圆润是有福气的象征

在中国传统文化中,面相是一个非常重要的概念。相信大家肯定听说过“相由心生”这个成语吧,这就是告诉我们,一个人的面貌其实是可以反映出他内心的状态和气质,也可以反映其性格、健康状况和运势等。而一个圆润的面相,则…

游泳耳机排行榜前四名,分享几款值得推荐的游泳耳机

游泳是一项全面锻炼身体的运动,然而,水的阻力有时让人感到运动的笨拙和单调。为了让游泳更具趣味性和挑战性,选择一款高品质的游泳耳机至关重要。以下是游泳耳机排行榜前四名,以及几款强烈推荐的游泳耳机,它们结合防水…

火狐谷歌兼容性问题整理

火狐谷歌兼容性问题整理 整理近期项目中遇到的火狐浏览器和谷歌浏览器兼容性问题。 数字输入框实现功能:限制输入框可输入内容类型为Number类型,其他类型不可输入。 (1)错误实现方法: 此方式在谷歌浏览器中可以实现&…

【QT-UI】

1.使用手动连接,将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中,在自定义的槽函数中调用关闭函数 #include "mainwindow.h" #include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), …

Python中级知识梳理

1. 文件操作 Python中的文件操作通常使用内置的open()函数来打开文件。以下是一个简单的示例: with open("file.txt", "r") as f:content f.read()print(content)在这个示例中,我们打开了名为"file.txt"的文件&#xf…

mac 快捷键

mac 程序坞 ctrlaltD:打开程序坞 调度中心 Ctrl键↑: 调度中心 Ctrl键↓:应用程序窗口 F11 : 显示桌面 输入法 Ctrl键空格: 切换输入法 Ctrl键Alt(Option)空格: 切换输入法 截屏 帮助 Ctrl键shift键/: 显示帮助

使用 CSS : 伪元素:after、过渡动画transition实现过渡效果(鼠标悬浮或点击 标签时,底部边框从左到右过渡)

首先&#xff0c;给 <span> 标签添加一个父元素&#xff0c;定义属性类名&#xff1a;nav-wrapper &#xff0c;父级设置相对定位。然后&#xff0c;使用 ::after 伪元素来创建一个与底部边框相同宽度的元素&#xff0c;初始时宽度为 0&#xff0c;通过过渡动画transitio…

「服务器」4.新手小白如何安装服务器环境-宝塔

刚开始初始化好的服务器&#xff0c;使用了阿里云客户端&#xff0c;看着网络脚本乱装&#xff0c;后来决定宝塔环境发现有重复的环境&#xff0c;遂决定重新初始化一下&#xff0c;然后重头干起。 重置服务器 将服务器关闭运行状态后&#xff0c;点击重新初始化云盘即可重新初…

GCF:在线市场异质治疗效果估计的广义因果森林

英文题目&#xff1a;GCF: Generalized Causal Forest for Heterogeneous Treatment Effects Estimation in Online Marketplace 中文题目&#xff1a;GCF&#xff1a;在线市场异质治疗效果估计的广义因果森林 单位&#xff1a;滴滴&美团 时间&#xff1a;2022 论文链接…

【数据库原理】期末突击(2)

有不会的题可以后台问我的哦&#xff0c;看见了就会回。祝大家期末心想事成。 数据库大题显而易见就只有几类&#xff0c;大家可以根据老师平时讲解的例题对应去解决一类型的题目。 前提知识; 候选码候选键关键字键码 关系代数&#xff1a;&#xff08;1&#xff09;传统&…

CST2024的License服务成功启动,仍报错——“The desired daemon is down...”,适用于任何版本!基础设置遗漏!

CST2024的License服务成功启动&#xff0c;仍报错——“The desired daemon is down…”&#xff0c;适用于任何版本&#xff01;基础设置遗漏&#xff01; CST2024的License服务成功启动后报错 若不能成功启动License服务&#xff0c;有可能是你的计算机名称带中文&#xff…

融云 CEO 董晗荣获 51CTO 「2023 年度科技影响力人物奖」

&#xff08;&#x1f446;点击获取《社交泛娱乐出海作战地图》&#xff09; 1 月 5 日&#xff0c;由知名 IT 技术媒体 51CTO 主办的第十八届“中国企业年终评选”正式揭晓榜单&#xff0c;融云 CEO 董晗荣获“2023 年度科技影响力人物奖”。关注【融云全球互联网通信云】了解…

【Android Studio】APP练手小项目——切换图片APP

本项目效果&#xff1a; 前言&#xff1a;本项目最终实现生成一个安卓APP软件&#xff0c;点击按钮可实现按钮切换图片。项目包含页面布局、功能实现的逻辑代码以及设置APP图标LOGO和自定义APP名称。 关于Android Studio的下载与安装见我的博文&#xff1a;Android Studio 最新…

蓝牙信标定位原理

定位原理&#xff1a;蓝牙信标的定位原理是基于RSSI蓝牙信号强度来做定位的。 根据应用场景不同&#xff0c;通过RSSI定位原理可分为两种定位方式 一、存在性定位 这种方式通常要求所需定位的区域安装一个蓝牙信标即可&#xff0c;手持终端扫描蓝牙信标信号&#xff0c;扫描…

huggingface下载太慢的问题

解决问题&#xff1a;huggingface网站模型文件太大&#xff0c;下载太慢 方式&#xff1a; huggingface镜像站网址 使用方法3的python脚本文件内容 import os# 下载模型 os.system(huggingface-cli download --resume-download THUDM/chatglm2-6b --local-dir /home/bennie…

Linux【C编程】 信号以及信号的处理方式

文章目录 1.什么是信号&#xff1f;1.1信号是内容受限的一种异步通信机制1.2信号由谁发出的&#xff1f;1.3信号由谁处理&#xff0c;如何处理 2.常见的信号3.进程对信号的处理3.1用signal函数处理SIGINT信号3.2使用sigaction 函数 4.alarm 和pause函数4.1 alarm函数详解4.2 pa…