news 2026/10/5 7:47:55

TensorFlow中indicator_column与embedding_column:类别特征处理详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TensorFlow中indicator_column与embedding_column:类别特征处理详解

在TensorFlow特征工程这块,feature_column一直是绕不开的基础组件。很多人一开始接触时,会被一堆名词劝退,尤其是indicator_column(指示列)和embedding_column(嵌入列)这两个,光看名字就有点绕。但说穿了,它们都是处理类别特征的工具,只是走的路子不一样。这篇文章我就结合自己的实际项目经验,把这两个column掰开揉碎讲清楚:它们各自解决什么问题、内部是怎么工作的、什么场景该用哪个、维度怎么定、有哪些容易踩的坑。不管你是刚开始学TensorFlow,还是已经在做推荐系统、CTR预估这类任务,这篇内容应该都能给你一些实在的参考。

1. 内容整体设计与思路拆解

1.1 特征工程里,类别特征为什么这么难搞

在讲indicator_column和embedding_column之前,得先想清楚一个问题:为什么类别特征在深度学习模型里这么特殊?

假如你有一个数值特征,比如“用户年龄”,28岁、35岁,这本身就是数字,喂给模型直接参与计算没问题。但类别特征不一样,比如“用户所在城市”,它是一堆离散的字符串:北京、上海、广州、深圳。模型是数学运算器,它不认识“北京”这两个字,它只认数字。所以必须有个环节,把这些离散的、非数值的类别,转换成模型能吃的数值形态。

这就引出了特征工程里最经典的一句话:类别特征的处理的本质,是“离散化后的向量化”。

那怎么向量化?最朴素的想法就是one-hot。比如全国有30个省级行政区,你把“用户所在省份”这个特征展开成30个维度的向量,用户在北京,北京那一位是1,其余29位是0。这个思路没错,indicator_column干的就是这件事。它把一个类别特征变成一个稀疏的0/1指示向量,所以叫“指示列”。

但问题来了,如果类别数量不是30,而是30万呢?比如商品ID,电商平台有几千万个SKU,你把商品ID做one-hot,一个特征就有几千万维。且不说内存爆炸,模型参数量也会大得离谱,训练根本跑不动。而且one-hot向量绝大多数位置都是0,这玩意儿又稀疏又高维,神经网络在这么高的维度上很难学到有效的模式。

这时候就得换个思路:与其把类别映射到一个巨大的稀疏向量上,不如把它映射到一个低维的稠密向量上,让模型自己去学这个类别的“表示”。这就是embedding_column干的事。

所以你看,这两个column解决的问题其实是同一个——类别特征的向量化——但策略完全不同:indicator_column追求“精确、简单、可解释”,代价是维度爆炸;embedding_column追求“低维、稠密、可学习”,代价是需要训练数据去学。理解了这一层,后面所有的细节都好办了。

1.2 indicator和embedding,本质上是两种不同的映射哲学

如果只记住一句话,那就是:indicator_column是one-hot的封装,embedding_column是可学习查表。

indicator_column听着高级,但剥开看就是one-hot。你给它一个类别特征,它内部做两件事:第一,把类别映射成整数ID,比如北京=0、上海=1、广州=2;第二,把这个整数ID转成一个向量,向量长度等于类别总数,ID对应的那个位置是1,其余是0。

embedding_column则是另一套逻辑。它也先把类别映射成整数ID,但这个ID不会用来做one-hot,而是拿去做查表。它维护一个形状为(类别总数,embedding维度)的矩阵,每一行就是一个类别对应的稠密向量。模型训练的时候,这个ID对应的那一行向量会被取出来,喂给后面的网络。同时,这个矩阵里的数值是可训练的,模型会通过反向传播不断调整它。训练完之后,每个类别就拥有了一个能表达自身特征的稠密向量。

用一个生活化的类比来解释:indicator_column像一个门牌号系统,每户人家一个编号,你报编号,邮递员就知道送哪家,精确但编号体系很占资源;embedding_column像一个“熟人推荐”,你跟他说你认识谁,他脑子里浮现出这个人的特征画像,然后根据画像去推荐,不精确但很灵活,而且这个画像会越处越准。

这个区别直接决定了它们的适用场景。类别数量少(几百以内)、业务上需要解释性强的,用indicator_column;类别数量大(几万、几十万甚至更多)、且类别之间存在语义关联的,用embedding_column。这个判断标准,后面我会详细展开。

2. 核心细节解析与实操要点

2.1 indicator_column底层在做什么

我们先从TensorFlow的源码层面,看看indicator_column到底是怎么实现的。知道了底层逻辑,你才能准确地预判它的行为。

indicator_column在TensorFlow中接收一个categorical_column作为输入,最常见的是categorical_column_with_vocabulary_list。它的核心流程是这样的:

  1. 输入一个原始值,比如字符串“北京”。
  2. 通过一个查找表(LookupTable),把“北京”映射成一个整数ID,假设ID=0。
  3. 根据类别总数,构造一个one-hot向量,在第0位上置1,其余位置置0。

这里有一个很关键的细节:这个one-hot向量是稀疏表示的。也就是说,它在内存里不会真的存一个几万维的数组,而是只存非零位置的索引和值。这个设计对性能至关重要,不然类别一多,内存直接爆炸。

indicator_column的API长这样:

import tensorflow as tf # 第一步:定义类别特征列 province_column = tf.feature_column.categorical_column_with_vocabulary_list( key='province', vocabulary_list=['北京', '上海', '广州', '深圳', '杭州'] ) # 第二步:包装成指示列 province_indicator = tf.feature_column.indicator_column(province_column)

这里有个初学者经常会犯的错:拿原始字符串特征直接去构造模型。比如你的数据集里“province”这一列是字符串,你不能直接把这一列丢给DenseFeatures,你必须先经过一个categorical_column做字符串到ID的映射,再丢给indicator_column做one-hot。原因很简单,模型只认数值,不认字符串。

再说一个实际项目里的经验。indicator_column虽然底层是稀疏的,但一旦你把它接入DenseFeatures,它会转换成稠密张量。比如你有1000个类别,那每个样本在模型里就会变成一个1000维的稠密向量,其中只有1个位置是1,其余999个是0。这会导致一个问题:如果这1000维后面直接接全连接层,那这一层的参数就是1000×隐藏单元数,计算量和内存消耗都很大。

所以在用indicator_column的时候,类别总数必须控制在一个合理的范围。我个人的经验是,2000以内问题不大,超过5000就要掂量掂量了。当然,这跟你的机器配置、模型复杂度都有关,但方向是明确的:indicator_column只适合“类别少、维度可控”的场景。

2.2 embedding_column的查表机制和训练方式

embedding_column的实现要比indicator_column复杂一些。它的核心是一个Embedding层,或者说一个可训练的查找表。

它的流程是:

  1. 输入原始字符串,比如“华为手机”。
  2. 通过查找表映射成整数ID,假设ID=888。
  3. 在一个形状为(类别总数,embedding_size)的矩阵里,取出第888行,得到一个长度为embedding_size的稠密向量。
  4. 这个向量作为特征进入后续的网络层。

API长这样:

import tensorflow as tf # 第一步:定义类别特征列(这里的num_buckets表示类别总数) product_column = tf.feature_column.categorical_column_with_hash_bucket( key='product', hash_bucket_size=100000 ) # 第二步:包装成嵌入列 product_embedding = tf.feature_column.embedding_column( categorical_column=product_column, dimension=16 )

这里有一个核心思想要理解:embedding矩阵的每一行,并不是预先定义好的,而是模型训练过程中学出来的。初始的时候,这些向量是随机初始化的,模型通过损失函数的梯度,不断调整这些向量,让相似的类别在向量空间里距离更近,不相似的类别距离更远。

举个实际的例子。做电商推荐的时候,商品类别是成千上万的,但“手机”和“耳机”这两个类别,在用户的点击行为上是有相关性的,模型在训练过程中会逐渐把这两个类别的embedding向量学得比较接近。这个“接近”不是我们手动定义的,而是数据驱动学出来的。

再说说embedding维度怎么选。这是很多人最纠结的问题,官方文档给了一个经验法则:embedding维度取类别数的4次方根。比如你有10000个类别,10000的4次方根是10,那embedding维度就取10。你有100万个类别,4次方根是31.6,那取32左右。

这个经验公式我在实际项目中验证过,虽然不是绝对最优,但作为起点非常靠谱。维度太小,表达能力不够,模型欠拟合;维度太大,不仅参数多,还容易过拟合,而且训练速度明显变慢。我自己做CTR预估的时候,类目特征差不多几万个,用16维效果就很不错了。

还有一个重要的技巧:embedding_column支持设置多个共享embedding。比如你有两个特征,一个叫“商品品牌”,一个叫“商品品类”,它们本身是两个独立的类别特征,但你可以让它们共享同一个embedding矩阵,只要它们的ID空间是一致的。这在迁移学习和多任务场景下特别好用。

2.3 两种column的对比,什么时候用哪个

我把这两个column放到一张表里对比,方便你直观地看差异:

对比维度indicator_columnembedding_column
输出形态高维稀疏0/1向量低维稠密向量
是否可学习否,固定映射是,随训练更新
内存占用随类别数线性增长,容易爆炸相对可控
解释性强,每一维都有明确含义弱,向量维度没有明确含义
适用类别数建议几千以内几万到几百万都可以
训练速度快,但后续层计算量大较慢,但整体效率高
典型场景小类别特征、需要强解释大规模类别特征、推荐系统

这表看完,你会发现一个有意思的现象:indicator_column看着简单,但它真正适合的场景其实很窄。只有当类别数少、且你明确知道每一维的含义很重要的时候,才应该用它。最常见的就是一些二值特征,比如性别、是否新用户、是否会员,这种类别数只有几个,用indicator_column完全没问题。

embedding_column则是工程上更常用的方案。尤其是推荐系统、广告点击率预估、搜索排序这些场景,特征动不动就是几万几百万的ID,这种场景下embedding_column几乎是唯一的选择。

不过我得提醒一句,如果真的只有几个类别,你别用embedding_column。比如性别就2个取值,你embedding成8维向量,不仅增加参数,效果也未必比indicator好。杀鸡不用牛刀,这是特征工程里的基本修养。

3. 实操过程与核心环节实现

3.1 完整示例:从原始数据到模型训练

光讲理论不实操等于白讲。我带大家走一个完整的例子,从构造数据开始,到训练一个模型,把indicator_column和embedding_column都用上。

假设我们要做一个简单的电影推荐模型。特征有三个:用户ID、电影ID、电影类型。标签是用户是否喜欢这部电影。前两个是大基数的类别特征,第三个是小基数的类别特征。

首先构造数据:

import pandas as pd import tensorflow as tf # 模拟数据 data = { 'user_id': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] * 100, 'movie_id': [101, 102, 103, 104, 105, 106, 107, 108, 109, 110] * 100, 'genre': ['动作', '喜剧', '科幻', '爱情', '动作', '科幻', '喜剧', '爱情', '动作', '科幻'] * 100, 'label': [1, 0, 1, 0, 1, 1, 0, 0, 1, 0] * 100 } df = pd.DataFrame(data)

接着构造feature_column。用户ID和电影ID基数大,用embedding_column;电影类型基数小,用indicator_column:

# 用户ID特征,做embedding user_id_col = tf.feature_column.categorical_column_with_hash_bucket( key='user_id', hash_bucket_size=1000 ) user_emb = tf.feature_column.embedding_column( categorical_column=user_id_col, dimension=8 ) # 电影ID特征,做embedding movie_id_col = tf.feature_column.categorical_column_with_hash_bucket( key='movie_id', hash_bucket_size=1000 ) movie_emb = tf.feature_column.embedding_column( categorical_column=movie_id_col, dimension=8 ) # 电影类型特征,类别少,用indicator genre_col = tf.feature_column.categorical_column_with_vocabulary_list( key='genre', vocabulary_list=['动作', '喜剧', '科幻', '爱情'] ) genre_ind = tf.feature_column.indicator_column(genre_col) feature_columns = [user_emb, movie_emb, genre_ind]

注意到这里有个细节:用户ID和电影ID在原始数据里是整数,但我们还是用categorical_column_with_hash_bucket来处理它们。因为ID虽然是个数字,但它本质上是离散的类别,不是有大小关系的数值。你不能说用户ID=2比用户ID=1“大”,这个“大”没有任何数学意义。所以类别特征的处理方式,不管输入是字符串还是整数,逻辑是一样的。

然后构造输入函数,转成Dataset格式:

def make_input_fn(data_df, num_epochs, shuffle=True, batch_size=32): def input_fn(): dataset = tf.data.Dataset.from_tensor_slices( (dict(data_df), data_df['label']) ) if shuffle: dataset = dataset.shuffle(10000) dataset = dataset.repeat(num_epochs).batch(batch_size) return dataset return input_fn train_input_fn = make_input_fn(df, num_epochs=10, shuffle=True)

最后构建模型。用DenseFeatures把feature_column统一处理,然后接几个全连接层:

# 构建模型 model = tf.keras.Sequential([ tf.keras.layers.DenseFeatures(feature_columns), tf.keras.layers.Dense(64, activation='relu'), tf.keras.layers.Dense(32, activation='relu'), tf.keras.layers.Dense(1, activation='sigmoid') ]) model.compile( optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'] ) # 训练 model.fit(train_input_fn, epochs=10, steps_per_epoch=100)

这里DenseFeatures是一个关键组件。它接收feature_column列表,在模型内部自动完成特征的解析、变换、拼接。indicator_column会在这里变成one-hot向量,embedding_column会在这里做查表并输出稠密向量,最后它们会被拼接成一个大的特征向量,作为后面全连接层的输入。

3.2 DenseFeatures的原理,以及它和feature_column的关系

上面例子里的DenseFeatures你可能会好奇,它到底干了什么?为什么把feature_column传给一个层,前面那一堆转换逻辑就全部搞定了?

这其实是TensorFlow 2.x一个非常优雅的设计。DenseFeatures本质上是一个预处理层,它在内部维护了一张图,每个feature_column都对应图上的一个操作节点。数据流进来的时候:

  1. 根据key从输入字典里取出对应的原始特征。
  2. 对类别特征执行“字符串/数字到ID”的映射。
  3. 根据column类型,执行one-hot(indicator_column)或查表(embedding_column)。
  4. 把所有column的输出拼接成一个大特征向量。

这个拼接逻辑要理解一下。假设你有3个特征:用户ID embedding后是8维,电影ID embedding后是8维,电影类型indicator后是4维,那最终DenseFeatures输出的向量就是20维。后面的全连接层接收的就是这20维输入。

这个设计好在哪里?好在你不需要手动去写一堆数据预处理代码。以前没有feature_column的时候,你得自己在数据管道里把字符串转成one-hot、把ID转成embedding索引,然后再手动拼特征,又麻烦又容易出错。现在只要在模型定义里声明feature_column,DenseFeatures帮你把预处理和模型训练无缝衔接起来。

不过要注意,DenseFeatures结合feature_column的方案,在TensorFlow 2.x里已经算是一种“经典但逐渐过时”的用法。TensorFlow官方现在更推荐用Keras的预处理层(如tf.keras.layers.CategoryEncoding、tf.keras.layers.Embedding)。但理解feature_column依然很有价值,因为大量存量项目、生产系统还在用它,而且它的设计思路和Keras预处理层一脉相承,学会了feature_column,再学Keras预处理层就是降维打击。

3.3 在真实项目中,embedding维度到底怎么定

维度选择在真实项目里不是一个纯理论问题,它受数据量、模型复杂度、训练资源等多重因素约束。我把自己的实操经验分享出来:

第一,从经验公式出发。类别总数开4次方根,得到一个初始值。比如你有10万类别,4次方根大约17.8,取16或者20都行。这个初始值可以作为第一次实验的基准。

第二,根据模型表现调整。如果训练集loss下降明显快,但验证集效果差,出现过拟合迹象,说明embedding维度偏大,可以按2的倍数缩小试试。反过来,如果训练集loss都降不下去,欠拟合明显,说明维度偏小,可以增大。

第三,考虑后续网络结构。embedding维度会直接影响DenseFeatures输出的总维度,进而影响第一层全连接的参数数量。如果embedding维度翻倍,第一层全连接的参数也会翻倍,训练和推理都会变慢。所以维度不是越大越好,够用就行。

第四,小技巧:做消融实验。同一个特征,分别用4、8、16、32维训练四个模型,对比验证集效果。这是最朴素但最有效的方法。我第一次做的时候,40万类别的特征,8维和16维效果差不多,那我当然选8维,训练速度快一倍。

顺带说一句,embedding列还有个常见配置项叫combiner。默认是'sqrtn',还支持'sum'和'mean'。这个配置在处理多值类别特征时很重要。比如一个用户看过10部电影,这10部电影的embedding向量需要合并成一个向量,combiner就决定了怎么合并。'sum'是直接求和,'mean'是取平均,'sqrtn'是求和后除以向量维度的平方根。不用纠结太多,默认'sqrtn'在多数场景下表现稳定,如果发现特征贡献度不合理,再试试'mean'。

4. 常见问题与排查技巧实录

4.1 categorical_column的几个子类型,到底选哪个

用了feature_column一段时间后,你会发现categorical_column有好几个变体,经常把人搞晕:categorical_column_with_vocabulary_list、categorical_column_with_vocabulary_file、categorical_column_with_hash_bucket、categorical_column_with_identity。我一个个说清楚。

categorical_column_with_vocabulary_list:你手动把所有类别值列出来。适合类别值已知且固定的情况,但缺点是vocabulary_list要自己维护,类别多了不现实。

categorical_column_with_vocabulary_file:跟上面一样,但类别值从文件读取。适合类别特别多、不方便写在代码里的情况。

categorical_column_with_hash_bucket:不维护具体的类别表,而是把输入做一个哈希,映射到0到hash_bucket_size-1之间。好处是省内存,坏处是存在哈希冲突,两个不同的类别可能映射到同一个ID上,造成信息混淆。

categorical_column_with_identity:输入本身就是整数ID,不需要额外映射。适合那种已经是ID的类别特征,比如用户ID、商品ID。

实际项目里怎么选?我个人的策略是:如果类别集合可控,用vocabulary_list;如果类别集合会动态增长,用hash_bucket;如果特征是整数ID,直接用identity。注意hash_bucket_size必须设置得比实际类别数大一些,留出余量,减少哈希冲突。比如你有5万类别,设成10万或者20万,问题不大。

4.2 踩过的坑:类别特征没经过categorical_column直接喂给模型

这个坑我见过太多次了,包括我自己早期也犯过。直接拿原始字符串特征去构造DenseFeatures,代码一跑直接报错,说找不到对应的处理逻辑。

核心原因:DenseFeatures只会处理你在feature_columns里定义的列。如果你定义了一个embedding_column使用的是'user_id'这个key,那输入数据里必须有'user_id'这个字段。但如果你定义的是数值列(numeric_column),输入数据里也必须有对应的key。但如果某个key你没有定义任何feature_column,那DenseFeatures不会理会它。

报错通常出现在类型不匹配上。比如你的输入是字符串,但feature_column定义的是numeric_column,那字符串转float直接失败。反过来,input是整数,但你用了categorical_column_with_vocabulary_list且vocabulary_list是字符串,也会报错。

解决方式很简单,但也容易忽略:在你构造Dataset的时候,确保输入数据的类型和你定义的feature_column匹配。字符串特征就交给categorical_column_with_vocabulary_list或hash_bucket,数值特征就交给numeric_column。不要混。

还有一个细节经验:DenseFeatures的输入必须是一个字典(dict),key对应特征名。很多新手在这里卡壳,写着写着就把字典变成了list或者Tensor,直接报错。记住这个规则:输入给DenseFeatures的,永远是一个特征名到值的字典。

4.3 哈希冲突的影响,以及怎么缓解

categorical_column_with_hash_bucket用起来方便,但哈希冲突是个绕不开的问题。假设你设了hash_bucket_size=1000,但实际类别有5000个,那平均每个bucket会被5个类别占用。这5个完全不同的类别会被当成同一个ID处理,模型无法区分它们。

哈希冲突的直接影响是特征表达能力的下降。比如“苹果”(水果)和“苹果”(手机品牌)如果冲突了,模型就分不清用户提到的是哪个苹果,特征信号就糊了。

缓解哈希冲突有这么几个手段:

第一,把hash_bucket_size设大,减少冲突概率。这个最直接有效。经验值是设成实际类别数的2到4倍。

第二,对输入值先做归一化或标准化。比如商品ID有大小写之分,统一转成小写再去哈希,能减少无效的类别数量。

第三,如果冲突造成了明显的模型效果问题,考虑用vocabulary_list方案,精确映射,不哈希。代价是内存占用偏高。

第四,用多个哈希函数,做bag of tricks。把特征哈希到两个不同的bucket集合里,拼接embedding结果,能在一定程度上对冲冲突的影响。这个技巧在工业界广告系统里很常见。

我自己的经验是,哈希冲突并没有想象中那么可怕。原因在于embedding本身是学习出来的,如果几个类别在数据中出现的模式相似,模型学出来的embedding也会相近,倒不一定是坏事。只有当冲突的类别行为模式差异很大时,才会有明显影响。所以先做实验,发现模型效果确实受影响,再考虑方案调整。

4.4 训练速度慢、内存爆了怎么办

embedding_column用多了,遇到的另一个典型问题是训练速度慢、内存占用高。原因很直接:embedding矩阵的大小是类别数乘以维度,类别数一多,矩阵就大,训练时反向传播还要更新这整块矩阵,慢是正常的。

针对内存问题,有几个实际操作手段:

一是使用分片(sharding)。在大规模分布式训练里,embedding矩阵可以按照ID范围切分到不同的机器上,每台机器只负责一部分ID的查表和更新。这个在TensorFlow里可以通过嵌入分区配置实现。

二是降低维度。这个前面说过了,做消融实验,选出够用的最小维度。

三是过滤低频类别。类别特征往往存在严重的“长尾现象”,绝大多数类别只出现一两次。这些低频类别不仅占用存储,还会引入噪声。建议在预处理阶段过滤掉出现次数少于阈值的类别,统一映射到一个单独的“未知”类别。这个操作对训练速度和模型效果的提升都很明显。

我做过一个案例,商品ID特征有200万类别,其中90%的出现次数不超过5次。过滤之后,类别数锐减到20万,embedding矩阵直接缩小10倍,训练速度上去了,模型效果还提升了。因为那些只出现一两次的类别本来就不足以学到可靠的embedding,留着只会增加噪声。

4.5 indicator_column遇到OOV(词表外)值怎么办

indicator_column用的是精确映射,但这也意味着它处理不了词表外的值。如果你的vocabulary_list里只列了1000个词,结果线上来了一个新的词,不在词表里,直接报错或者返回全零向量。

这个问题的解决方案是:在vocabulary_list里预留一个“未知”标记。比如在列表最后加上一个" ",然后在数据预处理阶段,所有不在词表里的值都替换成这个标记。这样indicator_column始终能处理所有输入,不会因为新类别而崩溃。

另外,categorical_column_with_vocabulary_list有一个参数叫num_oov_buckets,专门用来设置OOV桶的数量。比如你设num_oov_buckets=1,那所有词表外的词都会映射到同一个额外的桶里。这个参数配合词汇表,可以更优雅地处理OOV问题。

不过说实话,如果类别经常变动、新类别层出不穷,indicator_column也许本来就不是最合适的方案。这种场景下,用hash_bucket方式更稳妥,因为它天然具备处理新类别的能力。这是我在实际项目里反复验证过的结论。

最后聊点个人的实操体会

做了这么多年特征工程,我最大的感受是:feature_column这套东西,看起来只是API层面的封装,但它背后的设计思想特别值钱。你把indicator_column和embedding_column搞透了,其实就搞懂了“离散特征怎么做向量化”这个深度学习的核心问题。

如果说有什么实操上的建议,我会说:第一,别迷信经验公式,embedding维度一定要用消融实验去验证;第二,处理大基数类别特征时,先做低频过滤,收益立竿见影;第三,不要一条道走到黑,类别少就用indicator,类别多用embedding,别反着来。

后面我自己还在折腾的一个方向,是把feature_column和tf.data的数据管道深度结合,做流式特征工程。在超大规模推荐系统里,特征的实时更新是一个非常大的挑战,embedding的增量训练、低频特征的上线下架,都有很多细节可以打磨。这些内容如果大家感兴趣,我后面再单独写文章分享。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 7:47:52

TensorFlow feature_column深度解析:indicator_column与embedding_column对比与选型

做搜索、推荐、广告这类稀疏场景的,对 TensorFlow 的 feature_column 应该都不陌生。用户ID、商品ID、城市、性别、小时段,这些类别特征没办法直接塞给 Dense 层,总得先转成某种数值表达。我在 feature_column 上花过不少时间,印象…

作者头像 李华
网站建设 2026/10/5 7:47:42

SWD协议深度解析:从物理层到DP/AP寄存器的嵌入式调试本质

1. SWD不是“另一个JTAG”,而是为嵌入式现场调试量身重写的通信协议你拆过开发板,拧开过调试器外壳,也一定在Keil或STM32CubeIDE里勾选过“SWD”而不是“JTAG”——但有没有哪一刻,你盯着那个仅需两根线(SWDIO SWCLK&…

作者头像 李华
网站建设 2026/10/5 7:47:31

用DeepSeek与RAG构建政务政策问答系统:从知识库到满意度提升

简介:这份PDF围绕DeepSeek构建政务政策问答大脑,完整复盘了群众满意度提升38%的实战案例,适合政务数字化从业者、AI应用开发者及政策服务研究人员阅读。全包共1个文件,为30页PDF文档,压缩后大小约1.89MB,便…

作者头像 李华
网站建设 2026/10/5 7:46:14

SQL行值比较:从原理到实战,一个被低估的‘神仙写法’

写了好几年 SQL,自以为窗口函数、CTE、执行计划这些东西都摸得差不多了,结果在一次代码评审里被同事一行WHERE (a, b) > (x, y)给整愣了。当场第一反应是"这玩意儿能跑?",第二反应是"跑了之后结果对吗&#xff…

作者头像 李华
网站建设 2026/10/5 7:46:07

Linux内核GPD通用外设驱动框架深度解析与实战

1. 项目概述:这不是一次“读代码”,而是一次对嵌入式系统底层逻辑的现场解剖GPD——这个缩写在嵌入式开发、工业控制和边缘计算圈子里,从来不是泛泛而谈的概念。它特指Generic Peripheral Driver(通用外设驱动)框架&am…

作者头像 李华
网站建设 2026/10/5 7:46:04

C语言实现Picard与牛顿迭代法求根实战

1. 项目概述:用C语言亲手实现两种经典数值求根算法你是不是也经历过这样的时刻:在《数值分析》课本上看到Picard迭代和牛顿迭代法的公式,推导过程写得密密麻麻,可一合上书,脑子里只剩下一个模糊的“不断逼近”的印象&a…

作者头像 李华