恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

深度学习损失函数全解析:从MSE到Focal Loss的原理与应用

  • 首页
  • 资讯中心
  • /
  • 深度学习损失函数全解析:从MSE到Focal Loss的原理与应用

相关资讯

AI时代技术面试新策略:从背面经到AI协作实践 2026/8/29 5:18:56
【Matlab】建筑BIM模型参数化设计程序 2026/8/29 5:13:55
STM32U575/585硬件开发实战:低功耗与安全设计要点 2026/8/29 5:13:55

最新资讯

多传感器融合避障系统实战:激光雷达点云聚类与视觉检测融合解析
MATLAB数学规划模型实战:从线性规划到混合整数与非线性优化
STM32 DAC与DMA协同实现爱心波形生成:从原理到示波器观测
基于SEIR模型的疫情预测:Python实现与参数优化实战
[论文学习]ElasticBack:基于耦合触发-规则优化的LLM智能体技能隐蔽条件后门
外卖车免租金,智能电动车如何选?跑单成本与续航验证指南

今日推荐

云计算SPI三类服务模式是逐层抽象的关系:IaaS提供最底层的硬件资源,PaaS在IaaS基础上封装了开发运行环境,SaaS则进一步封装为可直接使用的软件
最新稳定版(Python 3.14):这是目前官方推荐的最新稳定版本。作为最后一个采用传统“3.x”命名的版本
etc目录下的profile.d文件目录设置环境变量和全局脚本shell

本周热门

Nextcloud 桌面客户端:把同步交给它,你只管改文件
如何将 HTML 转成 Word 文档且格式不丢失?html-to-docx 使用教程
Anki 批量操作卡片完整指南:一次搞定上千张,不再逐张修改

本月精选

如何用DamaiHelper实现演唱会门票的智能自动化抢购:完整技术解决方案指南
第4篇:59 倍性能差距的索引瓶颈定位——一次教科书级的全表扫描调优
终极歌词批量下载神器:5分钟解决离线音乐库歌词同步难题

深度学习损失函数全解析:从MSE到Focal Loss的原理与应用

发布时间:2026/8/29 5:18:56
深度学习损失函数全解析:从MSE到Focal Loss的原理与应用 1. 项目概述为什么我们需要深入理解Keras损失函数在构建任何一个神经网络模型时我们都会在model.compile()方法里遇到一个绕不开的参数loss。对于很多刚开始接触TensorFlow和Keras的朋友来说losscategorical_crossentropy或者lossmse可能只是一行需要记住的“咒语”。我们隐约知道它很重要因为它决定了模型优化的方向但往往也就止步于此了。tf.keras.losses这个模块就像一个装满各种“度量衡”的工具箱它告诉模型“你预测的结果和真实情况之间到底差了多少”这个“差了多少”的量化方式直接决定了模型学习的效率和最终能达到的高度。我自己在早期调参时就曾因为对损失函数理解不深而踩过不少坑。比如在一个类别极度不平衡的分类任务中我机械地使用了categorical_crossentropy结果模型迅速“学乖”了它发现只要把所有样本都预测为那个占多数的类别就能获得一个看起来还不错的损失值导致模型对少数类完全失效。后来深入研究了tf.keras.losses模块才发现里面有BinaryFocalCrossentropy这类专门应对不平衡数据的损失函数问题才迎刃而解。所以理解损失函数绝不是为了死记硬背几个API而是为了在面临具体问题时能做出最合适的选择甚至有能力自定义属于自己任务的“度量衡”。这篇内容我就结合自己多年的实战经验带你彻底拆解tf.keras.losses从核心原理到源码细节从内置函数到自定义实现让你不仅能“会用”更能“懂为什么用”和“知道怎么改”。2. 损失函数的核心逻辑与设计哲学2.1 损失函数的本质一个可微的指导信号损失函数也叫目标函数或代价函数它的数学本质是一个将模型的预测值y_pred和真实标签y_true映射为一个标量数值的函数。这个数值我们称之为“损失”Loss或“误差”Error。关键点在于这个函数必须是可微的。因为神经网络依赖反向传播算法来更新权重而反向传播的核心就是计算损失函数相对于每个网络参数的梯度。如果损失函数不可微梯度就无法计算模型也就无法学习。你可以把损失函数想象成给模型批改试卷的老师。模型每做一次预测交一次答卷损失函数就根据标准答案真实标签批改出一个分数损失值。但这个老师非常特别第一他批改得非常快而且是量化的第二他不仅告诉模型“你得了59分”还能清晰地指出“你在第3题的第2步计算错了导致这里扣分你应该这样改正……”。这后一个能力就是梯度。损失值指明了错误的程度而梯度指明了错误的方向和修正的力度。在tf.keras.losses的设计中所有内置损失函数都继承自一个基类Loss。这个基类保证了所有损失计算都遵循统一的调用规范loss_fn(y_true, y_pred)并且内部的计算逻辑是张量操作能够无缝融入TensorFlow的计算图自动支持梯度计算。理解这一点是后续进行自定义损失函数开发的基础。2.2 模块结构全景内置、封装与自定义tf.keras.losses模块的API结构清晰主要分为三个层次理解了这套结构你就能像查字典一样高效地使用它。第一层字符串别名。这是最常用、最便捷的方式。在model.compile(lossmse)中mse就是一个字符串别名。Keras内部维护了一个全局的损失函数注册表将这些字符串映射到对应的损失函数类。这种方式适合快速原型开发。常见的别名包括mse/mean_squared_errormae/mean_absolute_errorbinary_crossentropycategorical_crossentropysparse_categorical_crossentropy第二层损失函数类的实例。这是更灵活和强大的使用方式。你可以直接实例化一个损失函数类并传入一些关键参数进行配置。例如from tensorflow.keras import losses # 实例化一个带标签平滑的交叉熵损失 loss_fn losses.CategoricalCrossentropy(label_smoothing0.1) model.compile(lossloss_fn, optimizeradam)这种方式允许你对损失函数进行精细控制例如设置from_logits参数、调整reduction类型等。这些配置在字符串别名方式下是无法完成的。第三层自定义函数或子类。当内置损失函数无法满足你的特定需求时你就需要进入这个层次。你可以编写一个符合签名fn(y_true, y_pred)的函数或者继承tf.keras.losses.Loss基类创建子类。后者是更推荐的方式因为它能更好地兼容Keras的序列化、分布式训练等特性。# 方式一自定义函数简单情况 def my_mse_with_threshold(y_true, y_pred): error y_true - y_pred # 只有当误差大于某个阈值时才计算损失 thresholded_error tf.where(tf.abs(error) 0.5, error, 0.0) return tf.reduce_mean(tf.square(thresholded_error)) # 方式二继承Loss类推荐功能完整 class MyHuberLoss(losses.Loss): def __init__(self, threshold1.0, namemy_huber_loss): super().__init__(namename) self.threshold threshold def call(self, y_true, y_pred): error y_true - y_pred is_small_error tf.abs(error) self.threshold # Huber损失小误差用MSE大误差用MAE squared_loss tf.square(error) / 2 linear_loss self.threshold * tf.abs(error) - self.threshold**2 / 2 return tf.reduce_mean(tf.where(is_small_error, squared_loss, linear_loss)) def get_config(self): # 支持序列化 base_config super().get_config() return {**base_config, threshold: self.threshold}注意在自定义损失函数时务必确保内部所有操作都使用TensorFlow张量操作如tf.square,tf.reduce_mean而不是NumPy操作。因为前者能构建计算图并自动求导后者则不能。3. 分类任务损失函数深度解析分类任务是损失函数大显身手的主战场。不同的交叉熵变体应对着不同的数据形式和挑战。3.1 交叉熵家族从二分类到多分类交叉熵衡量的是两个概率分布之间的差异。在分类中y_true是真实的概率分布通常是one-hot编码y_pred是模型预测的概率分布。BinaryCrossentropy二分类的基石。它用于标签为0或1的标准二分类问题。其公式为loss -[y_true * log(y_pred) (1 - y_true) * log(1 - y_pred)]在Keras中y_pred的每个元素通常代表样本属于正类的概率范围应在0到1之间。这里有一个至关重要的参数from_logits。当from_logitsFalse默认时Keras假定你的y_pred已经是通过sigmoid激活函数处理后的概率值。损失函数内部会进行数值裁剪通常使用tf.clip_by_value防止对0或1取对数导致数值不稳定。当from_logitsTrue时Keras假定你的y_pred是模型最后一层线性层的输出即logits。损失函数内部会先应用sigmoid函数将其转换为概率再计算交叉熵。这是数值稳定性更高的做法推荐使用。尤其是在使用TensorFlow的混合精度训练时将softmax/sigmoid激活与交叉熵损失在数值稳定的环境下合并计算能有效减少精度损失并提升训练稳定性。# 推荐做法最后一层无激活损失函数设置 from_logitsTrue model tf.keras.Sequential([ ..., tf.keras.layers.Dense(1) # 注意这里没有 activationsigmoid ]) model.compile(losstf.keras.losses.BinaryCrossentropy(from_logitsTrue), ...)CategoricalCrossentropy单标签多分类的标准选择。用于每个样本只属于一个类别且标签是one-hot编码的情况例如图像分类中的手写数字识别。同样from_logits参数是关键。设置为True时它假定y_pred是logits并会在内部使用softmax进行转换。一个常见的错误是标签是one-hot编码但y_pred没有用softmax激活却又将from_logits设为False这会导致计算错误。SparseCategoricalCrossentropy处理整数标签的利器。当你的标签是整数形式如[2, 0, 4, ...]而非one-hot编码时就应用这个损失函数。它在计算效率上比先转换成one-hot再计算标准交叉熵要高因为避免了创建庞大的稀疏矩阵。其内部逻辑是将整数标签视为one-hot编码的索引计算方式在数学上与CategoricalCrossentropy等价但接口更简洁。# 假设有5个类别标签是0-4的整数 y_true tf.constant([2, 1, 0]) # 形状 (batch_size,) y_pred_logits model(x) # 形状 (batch_size, 5) # 使用稀疏交叉熵无需将y_true转为one-hot loss_fn tf.keras.losses.SparseCategoricalCrossentropy(from_logitsTrue) loss loss_fn(y_true, y_pred_logits)3.2 应对不平衡与难例Focal Loss与自定义策略标准交叉熵在处理类别不平衡或大量简单易分类样本时会遇到问题。损失值会被那些多数类或简单样本主导导致模型对少数类或难例的学习不足。BinaryFocalCrossentropy与CategoricalFocalCrossentropy这是Keras内置的Focal Loss实现。Focal Loss的核心思想是“关注难分类的样本”。它通过在标准交叉熵前乘以一个调制因子(1 - p_t)^gamma来实现。p_t对于正类是预测概率对于负类是1 - 预测概率。它表示模型对真实类别的预测置信度。gamma(focus_param)调节因子。gamma0时Focal Loss退化为标准交叉熵。gamma 0时对于预测置信度很高的样本p_t接近1调制因子接近0从而大幅降低其损失权重对于预测错的或置信度低的难例p_t小调制因子接近1损失权重基本保留。alpha类别权重用于进一步平衡正负样本。通常设置为少数类样本比例的倒数。# 用于极度不平衡的二分类任务如缺陷检测 loss_fn tf.keras.losses.BinaryFocalCrossentropy( gamma2.0, alpha0.25, # 假设正样本缺陷占比约25% from_logitsTrue )在实际项目中对于目标检测中前景-背景的极度不平衡或者医疗图像中病灶像素的稀疏性Focal Loss往往能带来显著的性能提升。我的经验是gamma通常在[0.5, 5.0]之间调节alpha需要根据你的数据集类别分布仔细调整。自定义加权交叉熵当你的不平衡不是简单的正负两类而是多个类别之间不平衡时可能需要自定义加权交叉熵。class WeightedCategoricalCrossentropy(losses.Loss): def __init__(self, class_weights, nameweighted_cce): super().__init__(namename) # class_weights: 一个列表或字典长度等于类别数 self.class_weights tf.constant(class_weights, dtypetf.float32) def call(self, y_true, y_pred): # 计算标准交叉熵 cce tf.keras.losses.categorical_crossentropy(y_true, y_pred, from_logitsFalse) # 根据样本的真实类别获取权重 # y_true是one-hot argmax得到类别索引 true_class_indices tf.argmax(y_true, axis-1) weights tf.gather(self.class_weights, true_class_indices) # 加权平均 weighted_loss cce * weights return tf.reduce_mean(weighted_loss)这里的class_weights可以根据每个类别在训练集中的频率来计算例如使用sklearn.utils.class_weight.compute_class_weight。4. 回归任务损失函数的选择与陷阱回归任务预测连续值损失函数衡量预测值与真实值之间的距离。4.1 MSE、MAE与Huber平滑与鲁棒的权衡MeanSquaredError最常用的L2损失。loss mean((y_true - y_pred)^2)MSE对误差进行平方因此它对离群点Outliers非常敏感。一个巨大的误差经过平方后会主导整个损失导致模型为了拟合少数离群点而扭曲了对整体趋势的把握。它的梯度是2*(y_pred - y_true)与误差成正比在接近最优解时梯度会变小更新变慢有利于收敛到精确解。适用于误差服从高斯分布、且离群点不多的场景。MeanAbsoluteError更鲁棒的L1损失。loss mean(|y_true - y_pred|)MAE对误差取绝对值因此对离群点的敏感度远低于MSE。它的梯度是常数sign(y_pred - y_true)这意味着无论误差大小每次更新的幅度是固定的方向不同。这使得训练初期可能收敛较慢且在最优点附近可能会因为梯度恒定而产生震荡。MAE适用于你希望模型对离群点具有鲁棒性的情况比如预测房价个别异常高的豪宅不应该过度影响模型。Huber两全其美的折中方案。Huber损失可以看作是MSE和MAE的平滑结合。它设定一个阈值delta。当误差绝对值|error| delta时采用MSE的行为二次项保证在接近目标时收敛良好。当|error| delta时采用MAE的行为线性项以降低离群点的影响。 其梯度在delta处是连续的这比单纯的MAE更利于优化。loss_fn tf.keras.losses.Huber(delta1.0)选择delta是一个超参数调优过程。通常可以从数据误差的标准差开始尝试。我的实操心得是在金融数据预测或传感器数据清洗等离群点常见的任务中Huber损失的表现通常稳定地优于单纯的MSE或MAE。你可以将delta作为一个可训练的参数虽然Keras内置的不支持但自定义损失函数可以实现让模型自己学习最佳的鲁棒性阈值。4.2 回归损失中的数值稳定性与尺度问题无论使用哪种回归损失都必须注意输入数据的尺度。MSE会对大的误差值进行平方如果你的标签y_true数值范围很大例如未经归一化的原始房价范围在几十万到几百万那么损失值会异常巨大导致梯度爆炸训练不稳定。标准做法是对回归任务的标签进行归一化或标准化。例如使用StandardScaler将标签转换为均值为0、标准差为1的分布。这样损失值会保持在一个合理的范围内。同时记得在模型预测后将输出反标准化回原始尺度进行评估。另一个技巧是使用对数空间损失。对于预测值恒为正且可能跨越多个数量级的数据如股票价格、人口数量预测相对误差比绝对误差更重要。此时可以在计算损失前先对y_true和y_pred取对数相当于计算MeanSquaredLogarithmicErrorMSLEKeras内置。MSLE对小值的预测误差惩罚更重对大值的预测误差更宽容符合很多业务场景的直觉。# 内置的MSLE等同于计算 mean((log(y_true 1) - log(y_pred 1))^2) loss_fn tf.keras.losses.MeanSquaredLogarithmicError()注意MSLE要求预测值和真实值都是非负的。5. 高级主题与自定义损失函数实战当你掌握了内置损失函数后自定义损失函数能让你解决更特定、更复杂的问题。5.1 理解Reduction参数与自定义损失的结构在实例化一个Keras损失函数或自定义Loss子类时reduction参数控制着损失计算完样本损失后如何聚合为最终的标量损失。主要有三种模式tf.keras.losses.Reduction.SUM_OVER_BATCH_SIZE默认计算批次内所有样本损失的平均值。loss sum(loss_per_sample) / batch_sizetf.keras.losses.Reduction.SUM计算批次内所有样本损失的总和。loss sum(loss_per_sample)tf.keras.losses.Reduction.NONE不进行聚合返回每个样本的损失值形状为(batch_size, ...)。这在需要为每个样本分配不同权重或者在多任务学习中非常有用。在自定义Loss类时call(self, y_true, y_pred)方法返回的应该是每个样本的损失即reductionnone时的形态。基类Loss的__call__方法会根据初始化时设定的reduction策略自动处理后续的聚合求和或平均。这是很多人在自定义损失时容易混淆的地方。5.2 实战实现一个带样本权重的对比损失假设我们在做语义相似度学习有一个三元组数据(anchor, positive, negative)。我们想拉近anchor和positive的距离推远anchor和negative的距离。标准的Triplet Loss是一个选择。但现在我们想根据positive样本的难度比如与anchor的初始距离动态调整权重难样本给予更高权重。class WeightedTripletLoss(losses.Loss): def __init__(self, margin1.0, softFalse, reductiontf.keras.losses.Reduction.AUTO, nameweighted_triplet_loss): super().__init__(reductionreduction, namename) self.margin margin self.soft soft # 是否使用软间隔版本 def call(self, y_true, y_pred): y_true: 在这里我们用不到可以传None但为了接口统一我们用它来传递样本权重。 假设y_true的形状是(batch_size, 1)每个元素是该三元组的权重。 y_pred: 模型输出形状(batch_size, embedding_dim)。 我们需要按照 [anchor, positive, negative, anchor, positive, negative...] 的顺序输入。 # 1. 分离出anchor, positive, negative的嵌入向量 batch_size tf.shape(y_pred)[0] // 3 anchor y_pred[0::3] # 第0, 3, 6...个元素 positive y_pred[1::3] # 第1, 4, 7...个元素 negative y_pred[2::3] # 第2, 5, 8...个元素 # 2. 计算距离 pos_dist tf.reduce_sum(tf.square(anchor - positive), axis-1) neg_dist tf.reduce_sum(tf.square(anchor - negative), axis-1) # 3. 计算基础三元组损失 if self.soft: # 软间隔版本使用logistic损失更平滑 basic_loss tf.math.log1p(tf.exp(pos_dist - neg_dist)) else: # 硬间隔版本 basic_loss tf.maximum(pos_dist - neg_dist self.margin, 0.0) # 4. 应用样本权重 (从y_true中获取) # 注意这里我们“滥用”了y_true来传递权重。更规范的做法是重写模型和训练循环。 # 这里仅为演示自定义损失中加权求和的思想。 weights y_true[:, 0] # 假设y_true形状为(batch_size, 1) weighted_loss basic_loss * weights # 5. 返回每个样本的加权损失。基类会根据reduction参数处理聚合。 return weighted_loss def get_config(self): return {**super().get_config(), margin: self.margin, soft: self.soft}这个例子展示了如何在损失函数内部进行复杂的张量操作拆分、距离计算并引入外部权重。在实际更复杂的训练流程中你可能需要自定义训练循环来灵活地提供这些权重。5.3 多任务学习中的损失组合与平衡在多任务学习中一个模型同时学习多个目标例如一个网络同时预测人的年龄和性别。每个任务都有自己的损失函数如年龄用MAE性别用BinaryCrossentropy。总损失通常是各个任务损失的加权和L_total w1 * L_task1 w2 * L_task2 ...。如何设置权重w_i是一个关键问题等权相加最简单但假设各损失值在同一尺度这通常不成立MAE可能几十交叉熵可能零点几。手动缩放根据经验或验证集效果手动调整权重。不确定性加权一种经典方法将每个任务的损失权重视为可学习的参数让模型在训练中自动学习最优权重。其核心思想是噪声大、难学的任务其损失权重应该自动降低。class MultiTaskLoss(losses.Loss): def __init__(self, num_tasks2, init_log_var0.0): super().__init__() # 将权重参数定义为对数方差保证其为正且训练稳定 self.log_vars tf.Variable(tf.fill([num_tasks], init_log_var), trainableTrue) def call(self, y_true_list, y_pred_list): y_true_list: 列表包含每个任务的真实标签。 y_pred_list: 列表包含每个任务的预测值。 total_loss 0.0 for i, (y_true, y_pred) in enumerate(zip(y_true_list, y_pred_list)): # 计算每个任务的损失例如任务0用MSE任务1用CrossEntropy if i 0: task_loss tf.keras.losses.mse(y_true, y_pred) else: task_loss tf.keras.losses.binary_crossentropy(y_true, y_pred) # 根据可学习的对数方差计算精度和加权损失 precision tf.exp(-self.log_vars[i]) # 精度 1 / (方差) weighted_loss precision * task_loss self.log_vars[i] total_loss weighted_loss return total_loss这里self.log_vars是可训练变量。损失由两部分组成precision * task_loss鼓励模型降低高精度低方差任务的误差self.log_vars[i]作为正则项防止精度无限增大。通过优化这个总损失模型会自动为不同噪声水平的任务分配合适的权重。6. 调试与性能优化指南6.1 损失函数相关的典型训练问题排查损失值为NaN或无限大Inf首要嫌疑交叉熵与logits设置错误。检查是否在最后一层使用了softmax/sigmoid激活同时在损失函数中又设置了from_logitsTrue。这会导致对概率值取对数可能产生NaN。反之如果没使用激活却设置from_logitsFalse也会出错。数据问题检查输入数据或标签中是否包含NaN、Inf或极端大的值。对回归标签进行归一化。学习率过大过大的学习率可能导致梯度爆炸使参数更新后输出进入导致损失计算无效的区域如对负数取对数。尝试降低学习率或使用梯度裁剪。自定义损失函数错误检查自定义损失中是否有除零、对非正数取对数等非法操作。使用tf.debugging.check_numerics在关键步骤添加检查。损失下降缓慢或不下降损失函数选择不当例如对于包含许多离群点的回归任务使用MSE损失可能被少数离群点主导导致模型在正常数据上拟合不佳。尝试换用Huber或MAE。输出层激活函数与损失函数不匹配例如在二分类任务中使用tanh作为输出层激活输出范围[-1,1]却使用BinaryCrossentropy期望输入[0,1]会导致梯度消失。确保激活函数输出范围与损失函数期望的输入范围一致。标签编码错误多分类任务中错误地使用BinaryCrossentropy或者SparseCategoricalCrossentropy的标签不是从0开始的连续整数。训练损失下降但验证损失上升过拟合这通常不是损失函数本身的问题而是模型复杂度过高或数据不足。但有时过于“严格”的损失函数如MSE可能会加剧模型对训练数据噪声的拟合。可以尝试加入正则化项L1/L2或者在损失函数中直接加入正则化自定义损失时。6.2 损失函数层面的性能优化技巧优先使用from_logitsTrue如前所述这能利用TensorFlow底层数值稳定的融合操作提高计算精度和速度尤其是在使用混合精度训练时。在自定义损失中使用向量化操作避免在call方法中使用Python循环for,while。始终使用TensorFlow的向量化操作如tf.reduce_*,tf.math.*, 广播机制。例如计算成对欧氏距离矩阵应使用tf.norm(a[:, None] - b[None, :], axis-1)而不是循环。利用tf.function装饰器谨慎对于非常复杂的自定义损失你可以尝试用tf.function装饰call方法将其编译为静态图可能提升执行效率。但要注意这可能会限制函数内Python代码的灵活性如打印调试并且首次调用会有编译开销。通常Keras内置的损失函数已经过高度优化自定义函数只要遵循向量化原则效率一般不是瓶颈。分布式训练兼容性如果你的自定义损失函数包含tf.Variable如多任务学习中的可学习权重请确保它能在分布式策略如MirroredStrategy下正确创建和更新。变量应在__init__中创建而不是在call中。理解tf.keras.losses模块是从“炼丹师”走向“机器学习工程师”的关键一步。它不再是黑箱里的魔法而是你手中可以精确调节的仪表盘。下次当你面对一个新的任务时不妨先花点时间思考我的数据有什么特点我的业务目标最关心什么误差什么样的损失函数能最好地引导模型学习这个目标想清楚这些问题你的模型训练就已经成功了一半。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号