Streamlit应用中MinMaxScaler输出全零的问题排查求助
问题
在Streamlit开发的应用中,使用scikit-learn的MinMaxScaler进行数据预处理后,所有输出值均为0。部分脚本代码如下:
contract = ['Proyek dibawah 100M','Proyek 100M-150M','Proyek 150M-500M','Proyek diatas 500M'] project_contract = st.selectbox("Select your project contract", contract) input_spec = pd.DataFrame(columns=['FC','SL','FA'], data=[[FC, SL, FA]]) input_area = pd.DataFrame(columns=['area_JAKARTA', 'area_JAWA', 'area_KALIMANTAN', 'area_PAPUA', 'area_SULAWESI', 'area_SUMATERA']) if area == 'Jakarta': input_area = pd.DataFrame(columns=['area_JAKARTA', 'area_JAWA', 'area_KALIMANTAN', 'area_PAPUA', 'area_SULAWESI', 'area_SUMATERA'], data=[[1,0,0,0,0,0]]) elif area == 'Jawa': input_area = pd.DataFrame(columns=['area_JAKARTA', 'area_JAWA', 'area_KALIMANTAN', 'area_PAPUA', 'area_SULAWESI', 'area_SUMATERA'], data=[[0,1,0,0,0,0]]) elif area == 'Kalimantan': input_area = pd.DataFrame(columns=['area_JAKARTA', 'area_JAWA', 'area_KALIMANTAN', 'area_PAPUA', 'area_SULAWESI', 'area_SUMATERA'], data=[[0,0,1,0,0,0]]) elif area == 'Papua': input_area = pd.DataFrame(columns=['area_JAKARTA', 'area_JAWA', 'area_KALIMANTAN', 'area_PAPUA', 'area_SULAWESI', 'area_SUMATERA'], data=[[0,0,0,1,0,0]]) elif area == 'Sulawesi': input_area = pd.DataFrame(columns=['area_JAKARTA', 'area_JAWA', 'area_KALIMANTAN', 'area_PAPUA', 'area_SULAWESI', 'area_SUMATERA'], data=[[0,0,0,0,1,0]]) elif area == 'Sumatera': input_area = pd.DataFrame(columns=['area_JAKARTA', 'area_JAWA', 'area_KALIMANTAN', 'area_PAPUA', 'area_SULAWESI', 'area_SUMATERA'], data=[[0,0,0,0,0,1]]) elif area == 'Bali & Nusa Tenggara': input_area = pd.DataFrame(columns=['area_JAKARTA', 'area_JAWA', 'area_KALIMANTAN', 'area_PAPUA', 'area_SULAWESI', 'area_SUMATERA'], data=[[0,0,0,0,0,0]]) if project_contract == 'Proyek dibawah 100M': input_project = pd.DataFrame(columns=['project_contract_150M-500M', 'project_contract_above 500M', 'project_contract_below 100M'], data =[[0,0,1]]) elif project_contract == 'Proyek 150M-500M': input_project = pd.DataFrame(columns=['project_contract_150M-500M', 'project_contract_above 500M', 'project_contract_below 100M'], data =[[1,0,0]]) elif project_contract == 'Proyek diatas 500M': input_project = pd.DataFrame(columns=['project_contract_150M-500M', 'project_contract_above 500M', 'project_contract_below 100M'], data =[[0,1,0]]) elif project_contract == 'Proyek 100M-150M': input_project = pd.DataFrame(columns=['project_contract_150M-500M', 'project_contract_above 500M', 'project_contract_below 100M'], data =[[0,0,0]]) for i in input_area.columns: input_area[i] = input_area[i].astype('float') for j in input_project.columns: input_project[j] = input_project[j].astype('float') input_submit = pd.concat([input_spec, input_area, input_project], axis=1) st.dataframe(input_submit) scaler = MinMaxScaler() input_submit_scaled = pd.DataFrame(scaler.fit_transform(input_submit.values), columns=input_submit.columns) st.dataframe(input_submit_scaled)
现象说明:
input_submit数据框包含FC/SL/FA数值列,以及地区、合同类型的独热编码列,存在正常数值和0/1值input_submit_scaled数据框所有列的值均为0
原因分析
问题核心在于用单条输入数据拟合MinMaxScaler。MinMaxScaler的缩放公式为:scaled_value = (x - min) / (max - min)
当仅传入单条数据时,该列的最小值min和最大值max完全相等,导致分母为0,最终所有缩放结果都为0。
解决方案
必须使用模型训练阶段的原始数据集拟合的scaler来处理新输入,而不是用单条数据重新训练。步骤如下:
- 提前在训练阶段拟合并保存scaler:
import joblib from sklearn.preprocessing import MinMaxScaler # 假设train_data是模型训练时的完整数据集,列名与input_submit一致 scaler = MinMaxScaler() scaler.fit(train_data) joblib.dump(scaler, 'minmax_scaler.pkl') # 保存scaler到本地文件
- 在Streamlit应用中加载已保存的scaler,仅调用
transform()处理输入:
# 替换原有scaler相关代码 scaler = joblib.load('minmax_scaler.pkl') input_submit_scaled = pd.DataFrame(scaler.transform(input_submit), columns=input_submit.columns) st.dataframe(input_submit_scaled)
额外优化建议
- 独热编码部分可改用
pandas.get_dummies或sklearn.OneHotEncoder自动生成,避免手动编写大量if-elif逻辑,减少冗余代码 - 确保输入的
FC/SL/FA数值范围与训练集一致,否则缩放结果会偏离预期
内容的提问来源于stack exchange,提问作者Alika Pratama
相关产品推荐
相关产品推荐

