使用Scikit-learn ColumnTransformer转换数据时遇AttributeError求助
在使用Scikit-learn的ColumnTransformer转换数据时触发如下错误:
AttributeError: 'ColumnTransformer' object has no attribute '_feature_names_in'
训练阶段代码
feature_columns_names = [ 'transaction_id', 'created_at', 'amount', 'device_model','device_mode', 'transaction_sum', 'daily_amt_ratio', 'monthly_amt_ratio' ] label_column = "is_fraud" non_scaled_cols = ['created_at','device_model','device_mode','transaction_id','is_fraud'] numeric_features = [col for col in list(feature_columns_names) if col not in non_scaled_cols] categorical_features = ['device_model','device_mode'] numeric_transformer = make_pipeline( SimpleImputer(strategy="constant", fill_value=0), StandardScaler()) categorical_transformer = make_pipeline( SimpleImputer(strategy="constant", fill_value="unknown"), OneHotEncoder(handle_unknown="ignore"), ) preprocessor = ColumnTransformer( transformers=[ ('num', numeric_transformer, numeric_features), ('cat', categorical_transformer, categorical_features)], remainder="drop") preprocessor.fit(data) joblib.dump(preprocessor, os.path.join(args.model_dir, "model.joblib"))
推理阶段代码
feature_columns_dtype = { 'transaction_id' :'object', 'created_at' :'object' , 'amount' :'float64', 'device_model' :'object' , 'device_mode' : 'object' , 'transaction_sum' : 'float64', 'daily_amt_ratio' : 'float64', 'monthly_amt_ratio' : 'float64', } label_column_dtype = {"is_fraud": "int64"} def merge_two_dicts(x, y): z = x.copy() # start with x's keys and values z.update(y) # modifies z with y's keys and values & returns None return z df = pd.read_csv('s3://data/dataset_sample.csv', header=None, names=feature_columns_names + [label_column], dtype=merge_two_dicts(feature_columns_dtype, label_column_dtype)) if len(df.columns) == len(feature_columns_names) + 1: # This is a labelled example, includes the ring label df.columns = feature_columns_names + [label_column] elif len(df.columns) == len(feature_columns_names): # This is an unlabelled example. df.columns = feature_columns_names model = joblib.load(os.path.join(model_dir, "model.joblib")) model.transform(df)
已确认训练与推理的Scikit-learn版本一致,特征名称匹配,数据加载正常,调用model.transform(df)时触发错误,求原因及解决办法。
核心原因
这个错误本质是训练时ColumnTransformer拟合的不是带列名的Pandas DataFrame,而是numpy数组或无列名的结构,导致模型实例未保存_feature_names_in_属性(错误信息中少写了末尾下划线,但本质是模型未记录特征列名)。
当推理时传入带列名的DataFrame,ColumnTransformer会尝试校验输入列名与训练时是否一致,但因训练时无列名记录,就会触发属性不存在的错误。
验证方法
检查训练阶段的data变量:如果是numpy数组、未设置列名的DataFrame,或者是用df.values这类方式传入的无列名数据,就会导致该问题。
解决办法
确保训练时传入带列名的DataFrame
训练阶段preprocessor.fit(data)中的data必须是带有正确列名的Pandas DataFrame,这样ColumnTransformer才会自动记录_feature_names_in_属性。推理时只传入模型需要的特征列
直接传入ColumnTransformer配置中用到的特征列,跳过列名校验步骤:# 只保留训练时用到的特征列 input_cols = numeric_features + categorical_features model.transform(df[input_cols])因为原配置中
remainder="drop"会丢弃其他列,所以直接传入目标列即可避免触发列名校验错误。补充训练时的列名记录(可选)
如果无法修改训练代码,可以手动给加载后的模型添加_feature_names_in_属性:model = joblib.load(os.path.join(model_dir, "model.joblib")) model._feature_names_in_ = np.array(feature_columns_names) model.transform(df)
内容的提问来源于stack exchange,提问作者MSS

