KNN算法实战指南:距离度量、K值选择与工业部署要点
1. KNN算法不是“邻居越多越好”而是“距离越准越稳”KNN全称K-Nearest Neighbors中文叫K近邻算法——这名字听着像社区居委会的邻里互助小组但其实它是机器学习里最朴素、最直白、也最容易被低估的分类与回归基石。我带过不少刚转行的数据新人第一课不讲数学推导先让他们手写一个5行代码的KNN分类器输入一个新样本算它和训练集里每个点的欧氏距离挑出最近的K个看这K个里哪类最多就判给哪类。做完之后问“你觉得K1好还是K100好”十有八九答“K越大越稳”。结果一跑真实数据——准确率直接掉15%。这才明白KNN不是靠堆人头取胜的投票游戏而是靠距离度量的合理性、K值的鲁棒性、以及数据分布的几何结构三者咬合运转的精密装置。它不建模、不拟合、不假设数据服从某种分布纯粹靠“眼见为实”做决策你离谁近你就像谁。这种“懒惰学习Lazy Learning”特性让它在小样本、高维稀疏、边界复杂但局部可分的场景中反而比SVM或逻辑回归更稳。比如医疗影像初筛某张CT片的纹理特征向量落在良性结节簇和恶性结节簇的交界区KNN不会强行画一条超平面把它硬分过去而是看它周围10个最近邻居里7个是恶性、3个是良性就给出0.7的概率预测——这个输出天然带置信度比“非黑即白”的硬分类更适合临床辅助判断。关键词KNN、算法不是泛泛而谈的术语标签而是指向一套以距离为尺、以邻域为据、以K为调节旋钮的决策逻辑。适合想快速理解监督学习本质的入门者也适合需要轻量级、可解释、低维护成本模型的工业现场工程师——比如产线传感器异常检测模型部署在嵌入式设备上连Python解释器都省了C语言实现200行搞定实时响应毫秒级。别被“算法”俩字吓住。它没有反向传播不调学习率不设隐藏层核心就三件事怎么算距离怎么选K怎么聚合邻居每一步背后都有现实约束欧氏距离在高维下会失效曼哈顿距离对异常值更鲁棒K太小易受噪声干扰K太大可能吞掉类别边界简单多数投票在类别不平衡时失灵加权投票又得设计权重函数。这些不是教科书里的习题而是我在汽车零部件缺陷检测项目里连续三天调参失败后盯着混淆矩阵发现的真相——当划痕类样本只占3%K5时新样本哪怕离划痕样本最近只要周围4个都是正常品就永远被判为“正常”。后来改用距离倒数加权最小支持度阈值才把召回率从62%拉到89%。所以这篇不是KNN百科而是我把十年间踩过的坑、验过的参数、压测过的边界全摊开给你看。2. KNN算法的核心设计逻辑与方案取舍2.1 为什么KNN是“懒惰学习”它的计算代价藏在哪很多人以为KNN训练快根本没训练过程就等于推理快。错。它的“懒”懒在训练阶段不做任何计算所有功夫都堆在预测时——每次预测都要把新样本和全部训练样本逐个计算距离。这意味着时间复杂度是O(N×d)N是训练集大小d是特征维度。我做过一个对比实验在10万条客户行为数据d50上KNN单次预测耗时平均12ms而同等规模的随机森林只要0.3ms。差距不是算法优劣而是范式不同随机森林把计算压力摊到训练期建树KNN则把压力全押在预测期查表。那为什么还要用因为它的“懒”换来了三样东西零假设、强可解释、易增量更新。零假设它不假设数据服从高斯分布也不要求特征线性可分只要“同类样本在空间中聚拢”这个基本事实成立它就能工作。我在做城市共享单车故障预测时GPS定位点报修时间戳车型编码构成的特征空间高度非线性SVM调参两周效果平平KNN用原始坐标直接跑AUC高出0.07。强可解释当模型说“这辆车明天大概率故障”你能立刻拉出它最近的5个邻居——全是同型号、同区域、上周刚维修过的车且其中3辆已报修。这种归因能力是深度学习黑箱永远给不了的。易增量更新新数据来了不用重训模型直接append进训练集。某物流公司的路径优化模块每天新增2000条轨迹用KNN做相似路径推荐数据库插完数据模型自动生效换成XGBoost就得 nightly retrain延迟4小时。所以方案取舍的本质是用计算资源换模型灵活性。当你的场景满足以下任一条件KNN就是合理选择数据量不大10万条或能接受预测延迟如离线报表生成需要模型决策过程完全透明如金融风控、医疗诊断数据流持续到达且无法容忍模型停机更新如IoT设备状态监控特征工程已做到极致但复杂模型仍过拟合——此时KNN的“无模型”特性反而成了抗过拟合的盾牌。提示KNN不是万能钥匙但它是检验数据质量的试金石。如果KNN在干净数据上效果很差大概率说明特征表达能力不足或者问题本身就不适合监督学习——这时候该反思业务定义而不是换算法。2.2 K值选择不是调参而是平衡偏差与方差的系统工程K值是KNN唯一的超参数但它的影响远超“选几个邻居”这么简单。它直接控制模型的偏差-方差权衡Bias-Variance TradeoffK1时模型方差极大对单个噪声点敏感偏差极小完美拟合训练集KN时模型偏差极大所有预测都一样方差极小完全不随数据波动。关键在于这个平衡点不能靠网格搜索暴力穷举。我在风电叶片振动分析项目里吃过亏用sklearn的GridSearchCV在K1~50间搜索交叉验证得分最高的是K32但上线后误报率飙升。复盘发现验证集里恰好混入一批传感器漂移导致的伪异常点K32把它们全包进邻域反而把真异常淹没了。真正有效的K值确定要分三步走第一步理论下限估算根据经验公式K ≈ √NN为训练样本数但这只是起点。更严谨的做法是计算最小有效邻域半径对每个训练样本找到它第k近邻的距离r_k取所有r_k的中位数作为参考半径。若K过大导致r_k 数据集直径的1/3说明邻域已覆盖整个空间失去局部性。第二步业务约束校准分类任务K必须为奇数避免平票且要大于最大类别的最小支持数。比如三分类中最少的一类只有8个样本那K至少取9否则该类永远无法胜出。回归任务K需保证邻居足够多以抑制噪声但又要小到能捕捉局部趋势。我处理温度传感器数据时发现K7时预测曲线光滑且不失真K15就开始抹平突变峰。第三步鲁棒性验证不只看准确率要看K值变化时指标的稳定性。画一张K值-准确率曲线理想状态是存在一段“平台区”——K在5~12之间准确率波动0.5%。这个平台区的中位数就是稳健K值。某次在银行信用卡欺诈检测中K3时准确率92.1%K5时92.3%K7时92.2%K9时骤降到89.7%——说明K5是黄金点再大就引入过多正常交易样本稀释了欺诈模式。注意K值与距离度量强耦合。用欧氏距离时K5合适换成余弦相似度后K往往要调大2~3倍因为余弦对向量长度不敏感邻域更“松散”。2.3 距离度量不是默认欧氏距离而是数据几何结构的翻译器距离是KNN的命脉但90%的人用着默认的欧氏距离却不知道它正在悄悄扭曲你的数据。欧氏距离公式是√Σ(x_i - y_i)²它隐含两个致命假设所有特征单位一致身高用米收入用万元直接相减毫无意义特征间相互独立房价地段×学区×楼层但欧氏距离把它们当正交轴处理。我在做电商用户分群时栽过跟头用原始特征年消费额、登录频次、平均停留时长直接算欧氏距离结果高消费但低活跃的用户和低消费但超高活跃的用户被判定为“近邻”——因为登录频次的数值范围0~300碾压了消费额0~50距离计算被单一特征主导。后来做了三件事标准化对每个特征做Z-scorex-mean/std让所有特征方差为1加权根据业务重要性赋权比如转化率权重设为2.0跳出率权重0.5换距离改用马氏距离它通过协方差矩阵Σ⁻¹修正特征相关性——公式变成√(x-y)ᵀΣ⁻¹(x-y)。马氏距离的效果立竿见影原本被淹没的“高价值沉默用户”消费高、登录少、停留长成功聚成独立簇精准匹配了运营的“唤醒高潜用户”策略。但马氏距离也有坑当样本数特征数时协方差矩阵奇异无法求逆。这时得用正则化马氏距离在Σ对角线上加λIλ0.01既保持可逆性又不过度平滑。其他常用距离的适用场景曼哈顿距离L1对异常值鲁棒适合传感器数据含脉冲噪声的场景切比雪夫距离L∞关注最大单维差异适合“短板效应”明显的系统如服务器集群只要CPU、内存、磁盘任一指标超阈值即告警余弦相似度忽略向量长度只看方向适合文本、图像特征向量TF-IDF、CNN embedding此时距离1-余弦值汉明距离专用于二进制特征如用户是否点击过某类广告统计不同位数。实操心得距离选择不是玄学而是数据物理意义的映射。先问自己哪些差异是业务上真正重要的是绝对数值差用欧氏还是相对比例差用余弦或是关键维度突破用切比雪夫答案决定了距离函数。3. KNN算法的核心细节解析与实操要点3.1 特征预处理不是可选项而是决定KNN生死的前置工序KNN对特征尺度极度敏感预处理不到位模型效果直接打五折。但预处理不是简单套StandardScaler就完事它包含四个不可跳过的环节1. 缺失值处理拒绝均值填充的“温柔陷阱”均值/中位数填充看似稳妥实则制造虚假邻域。比如用户年龄缺失填均值35岁这个“35岁”会和真实35岁用户产生强距离关联但业务上他们可能毫无共性。正确做法分三类数值型用KNN自身填补——对缺失样本先用其他完整特征找K个最近邻再用邻居的年龄均值填补。sklearn的KNNImputer就是干这个的类别型用众数填充但要加标记列如age_missingTrue让模型知道这是补出来的高缺失率特征30%直接删除KNN不需要特征数量堆砌需要的是信息密度。2. 特征缩放标准化 vs 归一化选错等于重跑标准化Z-score适用于特征近似正态分布如身高、温度公式(x-μ)/σ。优势是保留原始分布形态适合后续用马氏距离归一化Min-Max适用于有明确上下界如评分0~5分、占比0~100%公式(x-min)/(max-min)。优势是结果严格在[0,1]方便可视化鲁棒缩放RobustScaler当数据含大量异常值如订单金额有刷单巨单用中位数和四分位距替代均值和标准差公式(x-median)/IQR。我在处理外卖订单数据时用RobustScaler后K5的邻居质量提升明显——不再被几个万元订单带偏。3. 特征编码类别变量不是数字而是语义关系LabelEncoder把“北京0,上海1,广州2”但KNN会误以为北京和广州比上海更“接近”因为0和2的距离是21和2的距离是1。正确做法二值特征性别直接0/1编码低基数类别5类One-Hot编码生成哑变量高基数类别城市名1000个用目标编码Target Encoding用该类别下目标变量的均值替代如“北京”的编码值北京用户平均下单频次。注意要加平滑项防止小样本噪声。4. 特征选择删掉冗余特征比增加特征更重要KNN在高维空间面临“维度灾难”当维度d增大任意两点间距离趋近相等距离度量失效。解决方法不是降维PCA会破坏局部结构而是过滤无关特征。我的经验是计算每个特征与目标变量的互信息Mutual Information剔除MI0.01的特征对回归任务用Spearman秩相关系数剔除|ρ|0.1的特征加入业务规则比如预测贷款违约用户注册时长30天的样本一律剔除——这不是数据问题是风控策略。关键提醒预处理管道必须固化训练时用fit_transform预测时只能用transform。我见过太多人预测时重新fit scaler导致生产环境距离计算完全错乱。3.2 KNN的高效实现从暴力搜索到KD树再到LSH的实战抉择当N100万d100时暴力搜索Brute Force单次预测要算100万次距离耗时秒级无法接受。加速方案有三类选择取决于你的数据特性和硬件条件1. KD树K-dimensional Tree中小数据集的首选原理是递归划分特征空间构建二叉树搜索时剪枝。优势是精确查找、内存占用低劣势是维度20时效率断崖下跌“维度灾难”且只支持欧氏/曼哈顿距离。实操参数sklearn的NearestNeighbors中algorithmkd_treeleaf_size30叶子节点最小样本数太小树太深太大搜索慢适用场景N10万d20如客户地理围栏分析经纬度人口密度坑KD树不支持动态插入。训练后想增删样本得重建整棵树。2. Ball Tree高维数据的救星用超球面Ball而非超矩形Box划分空间对高维数据更友好。支持更多距离度量包括余弦且能处理非欧空间。实操参数algorithmball_treeleaf_size30metriccosine适用场景N50万d100~500如商品推荐用户embedding向量坑建树时间比KD树长30%但查询快2倍。3. 近似最近邻ANN百万级数据的唯一解当N100万必须接受“近似”换“速度”。主流方案AnnoySpotify开源用随机超平面分割构建森林查询时合并多棵树结果。优势是内存映射mmap加载快适合离线服务FaissFacebook开源GPU加速支持IVF倒排文件PQ乘积量化百亿向量毫秒级检索HNSWHierarchical Navigable Small World图结构索引精度高、速度快适合在线服务。我部署过一个实时新闻推荐系统用户向量d768N2000万。用Faiss IVF1024PQ16建库耗时2小时单次查询15ms准确率Top10召回率达92.3%换成暴力搜索单次要8秒。但ANN有代价需要调参IVF的聚类中心数、PQ的码本大小精度损失近似搜索可能漏掉真正的最近邻内存翻倍Faiss索引体积≈原始数据2倍。实操决策树N10万 → KD树N10万~100万d100 → Ball TreeN100万或d100 → Faiss/HNSW资源受限嵌入式→ 自研哈希表用局部敏感哈希LSH降维。3.3 分类与回归的差异化实现不只是改个参数那么简单KNN的分类和回归看似只差一个predict()函数但底层逻辑完全不同分类任务的核心投票机制的设计简单多数投票最常用但对类别不平衡敏感。解决方案距离加权投票邻居贡献 1/distance距离越近权重越大最小支持度约束设定阈值θ要求获胜类邻居数 ≥ K×θ如θ0.6否则返回“不确定”概率输出不直接给类别给每个类的概率 该类邻居数/K便于后续阈值调整。我在做工业质检时用距离加权最小支持度θ0.7组合把误杀率把良品判为不良从12%压到3.5%同时保持召回率98%——因为微小划痕样本少加权后它们的影响力被放大而最小支持度防住了噪声点偶然胜出。回归任务的核心聚合函数的选择均值聚合默认但对异常值敏感中位数聚合鲁棒性强适合含脉冲噪声的传感器数据截断均值去掉最高/最低20%邻居后再均值兼顾鲁棒与精度核加权回归用高斯核K(d)exp(-d²/(2σ²))加权σ由邻居距离自适应确定。某次预测光伏板发电功率用中位数聚合后RMSE比均值降低18%因为阴天突变数据被当作异常值过滤掉了。多输出场景不要用多个KNN堆叠比如同时预测设备温度、振动幅度、电流三个值。错误做法训练三个独立KNN。正确做法把三个目标当做一个3维向量用KNN找邻居再对邻居的目标向量做中位数聚合或用MultiOutputRegressor包装单个KNN内部自动处理多目标。关键细节分类任务中K必须≥类别数否则可能出现“某类邻居数为0”的情况回归任务中K不宜过小3否则预测抖动剧烈。4. KNN算法的完整实操流程与核心环节实现4.1 从零开始手写一个可调试的KNN分类器Python不依赖sklearn手写代码能让你彻底看清KNN的每一处细节。以下是一个精简但完整的实现含距离计算、K选择、投票逻辑import numpy as np from collections import Counter class SimpleKNN: def __init__(self, k3, distance_metriceuclidean): self.k k self.distance_metric distance_metric self.X_train None self.y_train None def fit(self, X, y): 训练只存储数据不计算 self.X_train np.array(X) self.y_train np.array(y) def _distance(self, x1, x2): 支持多种距离度量 if self.distance_metric euclidean: return np.sqrt(np.sum((x1 - x2) ** 2)) elif self.distance_metric manhattan: return np.sum(np.abs(x1 - x2)) elif self.distance_metric cosine: dot_product np.dot(x1, x2) norm_x1 np.linalg.norm(x1) norm_x2 np.linalg.norm(x2) return 1 - (dot_product / (norm_x1 * norm_x2 1e-8)) def predict(self, X): 预测对每个样本计算距离、找K近邻、投票 X np.array(X) predictions [] for x in X: # 步骤1计算当前样本与所有训练样本的距离 distances [] for i, x_train in enumerate(self.X_train): dist self._distance(x, x_train) distances.append((dist, self.y_train[i])) # 步骤2按距离排序取前K个 distances.sort(keylambda x: x[0]) k_nearest distances[:self.k] # 步骤3距离加权投票 class_weights {} for dist, label in k_nearest: weight 1 / (dist 1e-6) # 防止除零 class_weights[label] class_weights.get(label, 0) weight # 步骤4返回权重最大的类 pred_class max(class_weights, keyclass_weights.get) predictions.append(pred_class) return np.array(predictions) # 使用示例 X_train [[1, 2], [2, 3], [3, 3], [6, 7], [7, 8], [8, 8]] y_train [A, A, A, B, B, B] X_test [[2, 2.5], [7, 7.5]] knn SimpleKNN(k3, distance_metriceuclidean) knn.fit(X_train, y_train) preds knn.predict(X_test) print(preds) # [A B]这段代码的价值不在功能而在可调试性在_distance函数里加print(fdist({x1}, {x2}) {dist})能实时看到距离计算是否符合预期在k_nearest后加print(fK{self.k} neighbors: {k_nearest})验证邻居选取逻辑把weight 1 / (dist 1e-6)改成weight 1就退化为简单投票方便对比效果。实操心得手写KNN不是为了替代sklearn而是为了建立“距离-邻居-决策”的直觉。当你能徒手画出二维空间中K3的决策边界才算真正吃透KNN。4.2 工业级部署用scikit-learn构建生产就绪的KNN流水线生产环境不能用手写代码必须用成熟库工程化封装。以下是经过压测验证的完整流水线from sklearn.neighbors import NearestNeighbors from sklearn.preprocessing import RobustScaler, OneHotEncoder from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline from sklearn.impute import KNNImputer import joblib import pandas as pd # 1. 定义特征类型 numeric_features [age, income, login_days] categorical_features [city, device_type] target is_fraud # 2. 构建预处理管道 preprocessor ColumnTransformer( transformers[ (num, Pipeline([ (imputer, KNNImputer(n_neighbors5)), (scaler, RobustScaler()) ]), numeric_features), (cat, Pipeline([ (imputer, passthrough), # 类别型缺失用众数填充在fit时处理 (encoder, OneHotEncoder(handle_unknownignore)) ]), categorical_features) ], remainderdrop ) # 3. 构建完整流水线 knn_pipeline Pipeline([ (preprocessor, preprocessor), (knn, NearestNeighbors( n_neighbors7, algorithmball_tree, metricmanhattan, leaf_size30 )) ]) # 4. 训练与保存 df_train pd.read_csv(train_data.csv) X_train df_train.drop(columns[target]) y_train df_train[target] # 注意NearestNeighbors是无监督不使用y_train knn_pipeline.fit(X_train) # 保存流水线含预处理器和KNN索引 joblib.dump(knn_pipeline, knn_fraud_pipeline.pkl) # 5. 预测函数带业务逻辑 def predict_fraud_risk(new_sample, pipeline_pathknn_fraud_pipeline.pkl, k7): pipeline joblib.load(pipeline_path) # 获取邻居索引和距离 distances, indices pipeline.named_steps[knn].kneighbors( pipeline.named_steps[preprocessor].transform(new_sample) ) # 从原始训练集提取邻居标签 neighbor_labels y_train.iloc[indices[0]].values neighbor_distances distances[0] # 距离加权投票 weights 1 / (neighbor_distances 1e-6) weighted_vote np.average(neighbor_labels, weightsweights) # 业务规则风险分0.6才告警 risk_score weighted_vote alert_flag risk_score 0.6 return { risk_score: float(risk_score), alert_flag: bool(alert_flag), top_neighbors: indices[0][:3].tolist() # 返回前3个邻居ID供人工复核 } # 调用示例 new_case pd.DataFrame([{age: 25, income: 8000, login_days: 12, city: Shanghai, device_type: Android}]) result predict_fraud_risk(new_case) print(result) # {risk_score: 0.72, alert_flag: True, top_neighbors: [123, 456, 789]}这个流水线的关键设计KNNImputer用邻居均值填补缺失比全局均值更合理RobustScaler对抗收入等长尾特征的异常值OneHotEncoder(handle_unknownignore)防止线上出现训练时未见过的新城市BallTree Manhattan适配高维、含异常值的金融数据预测函数封装返回风险分、告警标志、邻居ID满足运维需求。注意事项NearestNeighbors不接受y参数它是无监督的。分类逻辑必须在预测函数中实现不能指望knn.predict()——因为sklearn的KNeighborsClassifier是另一套API这里我们追求的是最大灵活性。4.3 性能压测与瓶颈分析如何让KNN扛住每秒1000次请求KNN的性能瓶颈不在算法本身而在I/O和内存带宽。我做过一次全链路压测环境AWS c5.2xlarge8核32GB数据集N50万d50工具locust模拟并发请求结果单进程QPS120CPU利用率85%内存带宽打满优化手段分三层1. 模型层优化改用algorithmbruten_jobs-1暴力搜索在多核CPU上比BallTree快15%因为避免了树遍历开销距离计算向量化用scipy.spatial.distance.cdist替代循环提速3倍2. 系统层优化内存映射用np.memmap加载特征矩阵减少RAM占用进程池用concurrent.futures.ProcessPoolExecutor启动4个进程QPS升至4503. 架构层优化缓存热点查询用Redis缓存高频用户ID的邻居结果命中率65%整体QPS达980降级策略当Redis不可用自动切换到本地LRU缓存容量1000条最终架构Client → API Gateway → Redis Cache命中则返回 ↓未命中 Process Pool4 workers → KNN Model → Business Logic压测结论KNN的吞吐量瓶颈不在算法复杂度而在数据搬运速度。当特征矩阵超过10GBSSD读取成为瓶颈此时必须上NVMe SSD或内存数据库。实操警告不要在KNN预测函数里做耗时操作如数据库查询、网络调用。所有外部依赖必须前置完成KNN只做纯计算。5. KNN算法常见问题与排查技巧实录5.1 准确率忽高忽低先查这三处“隐形杀手”KNN效果不稳定90%的情况不是算法问题而是数据或配置的隐形缺陷问题1训练集和测试集分布不一致现象交叉验证准确率85%上线后跌到60%。根因测试集抽样方式错误。比如按时间顺序切分但训练集是2022年数据测试集是2023年促销季数据——用户行为模式已变。排查画训练集/测试集的特征分布直方图用KS检验p值0.05即分布差异显著。解法按业务逻辑切分如“老用户”和“新用户”分开建模或用时间序列交叉验证TimeSeriesSplit。问题2距离计算被某一特征主导现象修改某个特征的单位如把“收入”从元改为万元准确率暴跌。根因该特征数值范围远超其他特征距离计算被它垄断。排查计算各特征的标准差若最大std是最小std的100倍以上必有问题。解法强制标准化且在Pipeline中固定scaler参数避免线上/线下不一致。问题3K值在验证集最优但在业务指标上失效现象GridSearchCV选K15时F1最高但运营反馈误报太多。根因F1分数对类别不平衡不敏感而业务关注的是“精准率”Precision。排查画K值-精准率/召回率曲线找精准率95%且召回率下降最缓的K。解法自定义评分函数用make_scorer(precision_score, greater_is_betterTrue)。独家技巧用SHAP值解释单个预测。对KNN可以计算每个邻居对最终决策的贡献度——不是所有邻居都平等距离最近的那个可能贡献了70%的权重。5.2 “维度灾难”真实案例当KNN在100维失效时怎么办某智能仓储项目用激光雷达IMU电机编码器融合特征d128。KNN在验证集上AUC仅0.53随机水平。我们逐步排查Step1确认是否真为维度灾难计算所有样本对的平均距离和标准差发现std/mean 0.02理想值应0.1证明距离区分度丧失。Step2排除数据质量问题检查特征相关性矩阵发现32个IMU角速度特征两两相关系数0.95属于冗余特征。Step3针对性降维先用PCA保留95%方差d降至42AUC升到0.68再用UMAP非线性降维进一步压缩到d20AUC达0.79最后用领域知识筛选只保留与货架碰撞强相关的12个特征如横向加速度、转向角变化率AUC稳定在0.85。关键认知降维不是为了压缩而是为了凸显几何结构。PCA线性降维可能抹平关键非线性模式UMAP虽好但不可解释最佳方案是“PCA初筛领域知识精修”。5.3 KNN与其他算法的协同作战它从来不是单打独斗KNN最强大的地方是作为**其他