假设想要在一个包含一些分类参数的数据集上训练机器学习算法。(刚接触机器学习,我的想法是……)即使将所有分类数据转换为1-hot编码向量,这种编码映射在训练后如何被“记住”呢?
例如,在训练前将初始数据集转换为使用1-hot编码,假设
某列c的类别宇宙是{"good","bad","ok"},所以将行转换为[1, 2, "good"] ---> [1, 2, [1, 0, 0]],[3, 4, "bad"] ---> [3, 4, [0, 1, 0]],...
在训练模型后,所有未来的预测输入都需要对列c使用相同的编码方案。
那么在未来的预测中,数据输入如何记住这种映射(例如,“good”映射到索引0,等等)?(特别是,当计划使用keras
的RNN或LSTM模型时)我需要将它保存到某个地方吗(例如,python pickle)(如果是的话,我如何获得明确的映射)?或者有没有一种方法让模型自动处理分类输入,从而在训练和未来使用时只需输入原始标签数据?
如果这个问题中的任何内容显示了我对某事的严重困惑,请告诉我(再次强调,我对ML非常新手)。
** 我不确定这是否应该发布在https://stats.stackexchange.com/,但在这里发布是因为特别想知道如何处理此问题的实际代码实现。
回答:
我的想法是对训练/测试数据集D做类似的事情(使用Python和伪代码的混合):
- 做类似的事情
# 之前:D.schema == {num_col_1: int, cat_col_1: str, cat_col_2: str, ...} # 为每个分类列的不同标签分配唯一索引,并存储在一个新列中 # http://spark.apache.org/docs/latest/ml-features.html#stringindexer label_indexer = StringIndexer(inputCol="cat_col_i", outputCol="cat_col_i_index").fit(D) D = label_indexer.transform(D) # 之后:D.schema == {num_col_1: int, cat_col_1: str, cat_col_2: str, ..., cat_col_1_index: int, cat_col_2_index: int, ...}
对所有分类列进行操作
- 然后对D中所有这些分类名称和索引列,创建以下形式的映射
map = {} for all categorical column names colname in D: map[colname] = [] # 为所有分类值创建映射字典 # 见 https://spark.apache.org/docs/latest/sql-programming-guide.html#untyped-dataset-operations-aka-dataframe-operations for all rows r in D.select(colname, '%s_index' % colname).drop_duplicates(): enc_from = r['%s' % colname] enc_to = r['%s_index' % colname] map[colname].append((enc_from, enc_to)) # 为可能稍后出现的尚未见过的分类 # (不知道这是不是最佳实践,可能有其他方法,参见 https://medium.com/@vaibhavshukla182/how-to-solve-mismatch-in-train-and-test-set-after-categorical-encoding-8320ed03552f) map[colname].append(('NOVEL_CAT', map[colname].len)) # 按索引编码排序 map[colname].sort(key = lamdba pair: pair[1])
最终得到类似于下面的结果
{ 'cat_col_1': [('orig_label_11', 0), ('orig_label_12', 1), ...], 'cat_col_2': [(), (), ...], ... 'cat_col_n': [(orig_label_n1, 0), ...] }
然后可以使用它为任何后续数据样本行ds中的每个分类列生成1-hot编码向量。例如
for all categorical column names colname in ds: enc_from = ds[colname] # 为分类创建零向量以进行1-hot编码 col_onehot = zeros.(size = map[colname].len) for label, index in map[colname]: if (label == enc_from): col_onehot[index] = 1 # 在样本中为1-hot向量创建新列 ds['%s_onehot' % colname] = col_onehot break
- 然后可以将这个结构保存为pickle
pickle.dump( map, open( "cats_map.pkl", "wb" ) )
,以便在稍后进行实际预测时与分类列的值进行比较。
** 可能有更好的方法,但我认为需要更好地理解这篇文章(https://medium.com/@satnalikamayank12/on-learning-embeddings-for-categorical-data-using-keras-165ff2773fc9)。如果有任何更新,我会更新答案。