15分钟真正搞懂什么是“交叉熵损失函数”

大家好啊,我是董董灿。

在你刚学习神经网络的时候,有没有被一个名字叫做“交叉熵”的概念绕的云里雾里,以至于现在看到这个概念,依然很懵。

今天就来看一下,这个所谓的“交叉熵”到底是什么,以及它在神经网络扮演什么角色,神经网络中为什么要用它来做损失函数?

本篇文章前半部分会有一些公式的推导和解释(参考Naoki大佬博客,感兴趣可查看英文原文:https://naokishibuya.medium.com/demystifying-cross-entropy-e80e3ad54a8),你也可以直接跳到最后一部分查看神经网络中交叉熵损失的介绍。

1、交叉熵

“交叉熵”包含了“交叉”和“熵”这两部分,如果把这两部分都理解透彻,那这个概念也就没有什么神秘可言了。

“熵”在之前的一篇文章中已经说过了,如果对熵还不清楚的同学,可以看下这篇文章——对“熵”一知半解?带你揭开“熵”的神秘面纱

总的来说,大科学家香农大佬,定义了熵来计算“在确保信息传递过程不失真的情况下,传递信息所需要的最小编码大小”。

说的好理解一些,就是我向你传递一个消息,在确保没有任何信息丢失的情况下,我需要用到的最小的信息量。

在知道了熵的概念之后,下面从公式的角度一点点抽丝剥茧,来说明交叉熵。

对于存在概率分布的事件,计算熵的公式如下,这个公式在上篇文章解密熵中详细论述过:

其中,i 为发生的离散事件,根据不同的场景 i 会代表不同的含义,比如之前举的例子,东京的天气是阴天还是晴天,就属于在一天中会发生的离散事件。P(i)为发生这些事件的概率,对应到天气便是某一天降雨的概率,晴天的概率等。

上式熵的公式我们稍微变一下型,将负号和 log(P(i)) 看做一个变量,由于P(i)是每个事件 i 发生的概率,那么上式便可以理解为离散事件 i 发生概率的负对数(-log(p(i))的平均值(也叫期望)。

因此,熵的计算公式,便有了一个期望形式:

其中,x~P 表示用事件 x 服从概率分布 P,并且使用 P 来计算期望,同时熵一般用字母 H 表示,于是熵的公式可以写成如下形式:

简单点讲,熵这一概念,是用来表征在遵循特定概率分布的事件事件 x 发生时,传递信息需要用到的理论平均最小编码大小。

因此,只要我们知道发生事件的概率分布,我们就可以计算它的熵,如果我们不知道它的概率分布,那就没有办法计算熵。

所以,对于不知道概率分布的事件,需要想办法来估计概率分布,从而计算熵。

而利用估计的概率分布来计算熵,得到的是估计熵,这一点很重要,对我们接下来理解交叉熵很有帮助。

2、计算估计熵

假设现在向纽约发送东京的天气情况,并且希望在发送信息的过程中,将天气信息编码到最小来发送。

但我们遇到一个问题,那就是东京的天气我并不能百分百确定,也就是说天气在发生之前,我是不知道的,我所知道的,仅仅能通过天气预报的形式估计出一个天气概率。

假设在观察了东京一段时间的天气后,我大概得到了一个天气的概率分布Q,那么使用这个估计的概率分布Q,便可以计算出一个熵值(此时称为估计熵Estimated Entropy)为:

如果 Q 非常接近真实发生的概率分布,那么上式计算的结果(估计熵)便能更精确的反应传递信息需要的最小编码大小。

但是,这种计算估计熵的公式,存在两种不确定性。

首先,x~Q 表示我们利用概率分布 Q 来计算期望,而 Q 是估计出来的,可能会与实际概率分布 P 相差很大。

其次,计算最小编码时,是基于估计的 Q 来计算的概率分布的负对数 -log(Q),而这也不会是100%准确的。

由此可见,Q 即影响期望,又影响最小编码估计,因此,计算出的估计熵并不能反应出什么,继而将估计熵和真实熵进行对比,也无法得出什么有意义的结论。

那怎么办呢?先回到这件事的本质上,其实一开始我们关心的,是在传递信息的时候,如何让编码大小尽可能小,也就是传递的信息量最小。

因此,我们既然有了熵的理论了,可以基于熵的理论,将我们的计算的编码大小与理论最小的编码进行比较即可,而不去考虑期望,将Q的影响由双变量变为单变量。

举个例子,假设在观察一段已发生的东京天气后,得到了天气发生的真实分布P ,我们可以使用概率分布 P 来计算真实的平均编码大小,而利用天气预报中的概率分布 Q,来计算未发生的事件所需要的编码大小。

这便是基于概率事件 P 和 Q 之间的交叉熵:

上式中,计算期望我们使用了相同的真实概率分布 P,因此此时比较两个熵的大小,实际上比较的是 -logQ 和 -logP 的大小,也就是理论最小编码和实际编码大小。

说的通俗一点,交叉熵在交叉的检查编码大小,这就是交叉熵中“交叉”的意思。

3、交叉熵 ≥ 熵

通常,交叉熵用H表示如下:

H(P, Q) 表示使用 P 计算期望,使用 Q 计算编码大小。

因此,H(P, Q) 和 H(Q, P) 不一定相同,但如果 Q=P,在这种情况下H(P, Q) = H(P, P) = H(P)并且变成熵本身。

有一点需要注意的事,在计算期望时应该使用真实概率P,因为它反映了真实事件的分布。而在计算编码大小时应该使用Q,因为它反映了对消息进行编码的大小。

可以看到,如果估计的概率分布是完美的,也就说如果估计的概率分布 Q 刚好等于 P,那么此时 H(P, Q)=H(P),否则 H(P, Q) > H(P)。

以上便是交叉熵和熵之间的联系,不知各位同学看懂了没,没看懂没关系,接着看下下面的实例,或许你会对交叉熵有更深刻的理解。

4、交叉熵作为损失函数

假设有一个动物图像数据集,其中有五种不同的动物,每张图像中只有一只动物。

来源:https: //www.freeimages.com/

我们将每张图像都使用 one-hot 编码来标记动物。对one-hot编码不清楚的可以移步这里有个你肯定能理解的one-hot

上图是对动物分类进行编码后的表格,我们可以将一个one-hot编码视为每个图像的概率分布。

第一个图像是狗的概率分布是 1.0 (100%)。

对于第二张图是狐狸的概率分布是1.0(100%)。

以此类推,此时,每个图像的熵都为零。

换句话说,one-hot 编码标签 100% 确定地告诉我们每张图像有哪些动物:第一张图片不可能 90% 是狗,10% 是猫,因为它100%是狗。

现在,假设有一个神经网络模型来对这些图像进行分类,在神经网络执行完一次推理,或者完成一轮训练迭代后,它可能会对第一张图像(狗)进行如下分类:

该分类表明,第一张图像中狗占 40%,狐狸占 30%,马占 5%,老鹰占 5%,松鼠占 20%。

但是,单从图像标签上看,它100%是一只狗,标签为我们提供了这张图片的准确的概率分布。

那么,此时模型分类预测的效果如何呢?我们可以计算利用标签的one-hot编码作为真实概率分布 P,模型预测的结果作为 Q 来计算交叉熵:

结果明显高于标签的零熵,说明预测结果并不是很好。

先不急,继续看另一个例子。

假设模型经过了改良,在完成一次推理或者一轮训练后,对第一张图得到了如下的预测,也就是说这张图有98%的概率是狗,这个标签的100%已经差的很少了。

我们依然计算交叉熵:

可以看到交叉熵变得很低,随着预测变得越来越准确,交叉熵会下降,如果预测是完美的,它就会变为零。

基于此理论,很多分类模型利用交叉熵作为模型的损失函数。

在机器学习中,由于多种原因(比如更容易计算导数),对数的计算大部分情况下是使用基数 e 而不是基数 2 ,对数底的改变不会引起任何问题,因为它只改变幅度。

<think>我们首先需要生成一个金融交易欺诈检测数据集。数据集应包含以下字段:地址、金额、交易类型、交易时间,以及标签(是否为欺诈交易)。然后,我们将设计一个简单的模型(例如逻辑回归)来检测欺诈交易,并计算损失函数,绘制损失曲线。步骤:1.生成模拟数据集2.数据预处理(包括特征工程,例如对分类变量进行编码,对时间进行处理等)3.划分训练集和测试集4.构建模型5.训练模型并记录每次迭代的损失6.绘制损失曲线注意:由于是模拟数据集,我们不会使用真实的欺诈数据,但会模拟一些欺诈交易的特征。我们将使用以下库:-pandas用于数据处理-numpy用于数值计算-sklearn用于模型构建和数据集划分-matplotlib用于绘图模拟数据的特征:-地址:我们可以生成一些随机的地址(例如用字符串表示),但为了简化,我们可以用地址的类别进行编码(这里我们假设地址是分类变量,比如代表不同的国家或地区)。我们使用整数编码即可,因为地址本身没有顺序关系,但在模型中可以尝试使用one-hot编码,但为了简化模型,我们可能先使用整数编码或考虑嵌入,但这里我们简单处理为类别特征,使用one-hot编码。-金额:交易金额,欺诈交易可能金额较大或者较小,我们可以设置一个分布。-交易类型:分类变量,例如在线支付、转账、存款等。-交易时间:可以包括日期和时间。我们可以提取小时(因为欺诈可能在夜间更频繁)或者是一周的第几天等。这里我们将交易时间转换为从某个参考时间开始的小时数(或分钟数),但为了简单,我们可以使用小时作为特征。生成模拟数据时,我们将设定一些规则来生成欺诈交易:例如:-在凌晨1-5点的交易欺诈概率更高-某些交易类型(如国际转账)欺诈概率更高-大额交易(例如大于10000)欺诈概率更高我们将生成一个包含1000条记录的数据集,其中欺诈交易大约占10%。模型:使用逻辑回归,因为简单且易于解释。损失函数使用二元交叉熵。注意:在金融欺诈检测中,数据通常是不平衡的(欺诈交易很少),所以我们可能需要使用一些处理不平衡数据的方法,但这里我们主要关注损失曲线,因此暂时先不处理。接下来,我们开始代码:</think> ### 金融交易欺诈检测数据集生成与Loss曲线分析 以下是完整的解决方案,包含数据生成、模型实现和Loss曲线绘制: ```python import pandas as pd import numpy as np import matplotlib.pyplot as plt from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler, OneHotEncoder from sklearn.compose import ColumnTransformer from sklearn.linear_model import LogisticRegression from sklearn.metrics import log_loss # 1. 生成欺诈检测数据集 def generate_fraud_data(num_samples=5000): np.random.seed(42) # 模拟参数 fraud_ratio = 0.15 # 欺诈交易占比 # 生成地址(虚拟城市) cities = ['New York', 'London', 'Tokyo', 'Singapore', 'Sydney'] addresses = np.random.choice(cities, num_samples, p=[0.3, 0.25, 0.2, 0.15, 0.1]) # 生成交易金额(正态分布,欺诈交易金额更大) normal_amounts = np.random.normal(500, 200, num_samples) fraud_amounts = np.random.normal(1500, 500, num_samples) amounts = np.where(np.random.random(num_samples) > fraud_ratio, normal_amounts, fraud_amounts) # 生成交易类型 trans_types = np.random.choice(['Online', 'ATM', 'POS', 'Bank Transfer', 'Wire'], num_samples, p=[0.4, 0.2, 0.15, 0.15, 0.1]) # 生成时间特征(2023年) times = pd.date_range('2023-01-01', periods=num_samples, freq='5min') hours = np.random.choice(np.arange(24), num_samples, p=np.sin(np.pi*np.arange(24)/24)+0.5) hours /= np.sum(np.sin(np.pi*np.arange(24)/24)+0.5) # 欺诈标签(基于规则+随机性) fraud_labels = ( # 规则1:高额交易风险更高 (amounts > 1200) * 0.4 + # 规则2:凌晨交易风险更高 ((pd.DatetimeIndex(times).hour > 1) & (pd.DatetimeIndex(times).hour < 5)) * 0.3 + # 规则3:特定交易类型风险更高 np.isin(trans_types, ['ATM', 'Wire']) * 0.3 + np.random.uniform(-0.2, 0.2, num_samples) ) > 0.5 # 添加噪声 fraud_labels = fraud_labels ^ (np.random.random(num_samples) < 0.05) # 创建DataFrame data = pd.DataFrame({ 'Address': addresses, 'Amount': np.abs(amounts.round(2)), 'TransactionType': trans_types, 'Timestamp': times, 'IsFraud': fraud_labels.astype(int) }) return data # 2. 生成数据 fraud_data = generate_fraud_data(10000) print(f"Dataset shape: {fraud_data.shape}") print(f"Fraud rate: {fraud_data['IsFraud'].mean():.3f}") fraud_data.head() # 3. 数据处理 X = fraud_data.drop('IsFraud', axis=1) y = fraud_data['IsFraud'] # 提取时间特征 X['Hour'] = pd.to_datetime(X['Timestamp']).dt.hour X = X.drop('Timestamp', axis=1) # 定义预处理流水线 numeric_features = ['Amount', 'Hour'] categorical_features = ['Address', 'TransactionType'] preprocessor = ColumnTransformer( transformers=[ ('num', StandardScaler(), numeric_features), ('cat', OneHotEncoder(handle_unknown='ignore'), categorical_features) ]) # 4. 划分数据集 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=42, stratify=y) # 数据预处理 X_train_processed = preprocessor.fit_transform(X_train) X_test_processed = preprocessor.transform(X_test) # 5. 创建模型(带Loss记录) class LogRegWithLossHistory(LogisticRegression): def __init__(self, **kwargs): super().__init__(**kwargs) self.loss_history = [] def fit(self, X, y): # 添加初始状态(随机权重) self.coef_ = np.random.randn(X.shape[1], 1).T self.intercept_ = np.random.randn(1) # 使用自定义训练循环 lr = 0.1 iterations = 300 for i in range(iterations): # 计算预测概率 z = X @ self.coef_.T + self.intercept_ y_pred = 1 / (1 + np.exp(-z)) # 计算梯度 dw = (1/len(X)) * X.T @ (y_pred - y.values.reshape(-1, 1)) db = (1/len(X)) * np.sum(y_pred - y.values.reshape(-1, 1)) # 更新参数 self.coef_ -= lr * dw.T self.intercept_ -= lr * db # 记录损失(二元交叉熵) loss = log_loss(y, y_pred) self.loss_history.append(loss) # 学习率衰减 if i % 50 == 0: lr *= 0.8 # 6. 训练模型并记录Loss model = LogRegWithLossHistory() model.fit(X_train_processed, y_train) # 7. 绘制Loss曲线 plt.figure(figsize=(12, 6)) plt.plot(model.loss_history) plt.title('Training Loss Curve', fontsize=14) plt.xlabel('Iteration', fontsize=12) plt.ylabel('Binary Cross-Entropy Loss', fontsize=12) plt.grid(alpha=0.3) plt.yscale('log') # 对数坐标便于观察 plt.show() # 8. 保存数据集 fraud_data.to_csv('fraud_transaction_dataset.csv', index=False) ``` ### 代码解释: 1. **数据生成**: - 模拟了地址、金额、交易类型和时间四个关键特征 - 地址从主要金融城市中随机选择(加权概率) - 交易金额分为正常交易(均值为500)和欺诈交易(均值为1500) - 使用时间特征(小时)作为重要欺诈指示器 - 欺诈标签基于组合规则生成(大额+凌晨+特定交易类型) 2. **特征工程**: - 从时间戳中提取交易小时 - 使用`ColumnTransformer`构建预处理管道: - 数值特征(金额、小时)标准化处理 - 类别特征(地址、交易类型)进行独热编码 3. **建模与损失计算**: - 继承LogisticRegression并自定义训练过程 - 实现手动梯度下降(控制学习率) - 每轮迭代计算二元交叉熵损失: ``` Loss = -1/N * Σ[y_i*log(p_i) + (1-y_i)*log(1-p_i)] ``` - 添加学习率衰减策略(每50轮衰减20%) 4. **Loss曲线**: - 使用对数坐标展示损失下降过程 - X轴为训练迭代次数 - Y轴为二元交叉熵损失值 - 曲线展示了模型从随机权重开始的收敛过程 ### 生成数据集示例: | Address | Amount | TransactionType | Timestamp | IsFraud | |-------------|--------|-----------------|---------------------|---------| | London | 1482.75| ATM | 2023-01-01 00:00:00 | 1 | | New York | 456.32 | Online | 2023-01-01 00:05:00 | 0 | | Tokyo | 732.98 | POS | 2023-01-01 00:10:00 | 0 | | Singapore | 1923.50| Wire | 2023-01-01 00:15:00 | 1 | --- **
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

董董灿是个攻城狮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值