250KB心理状态识别数据集:从数据预处理到模型部署的完整实战
2026/9/15 2:07:33 网站建设 项目流程

简介:面向机器学习初学者与数据分析人员的心理状态识别实战资源包,基于Python完成从数据清洗、特征编码到模型训练与评估的完整流程,覆盖分类、聚类、降维与可视化等多种任务。压缩包共7个文件,以5个源代码为核心,另含1个CSV格式的完整数据集和1个说明文档,代码约40.55KB,压缩包整体约109KB,可直接解压后按readme顺序运行。已有53人学习下载。代码手工整理、无语法错误,使用pandas、numpy、seaborn等完成探索分析,并实现AdaBoost、随机森林、XGBoost、CatBoost、LightGBM、KMeans、PCA/t-SNE、神经网络及Voting集成等方法;同时引入OneHotEncoder、CatBoostEncoder、PowerTransformer、KFold交叉验证、混淆矩阵和网格搜索调参,便于对比不同模型在心理状态分类任务上的精度、召回与F1差异。资源包含完整数据集和readme说明,方便快速复现实验、替换自有数据,也适合课程设计或竞赛练手。

1. 一个 250KB 心理状态识别数据集,如何跑通完整的 AI 预测链

在 AI 实战中,心理状态识别最难的往往不是模型,而是把“心里状态”这个模糊概念转成模型能学习的标签。拿到一个只有 250.72 KB 的完整数据集,我第一反应是看它的规模配比:这个体积大概率不是原始脑电波或视频帧,而是结构化的量表、行为特征或经过压缩的时序特征文件。好消息是,这类小样本数据集足以承载从数据清洗、特征筛选、多模型对比到预测代码落地的完整链路;坏消息是,样本量一旦过千,直接套深度模型很容易过拟合。这篇文章不是某个现成项目的逐行解读,而是站在“如果我自己重做这个项目”的角度,把这类心理状态识别实战的通用解法讲清楚:数据怎么拆、5 个源代码通常怎么分工、参数怎么设、预测结果怎么验证。

2. 心理状态识别数据集的结构分析与预处理

2.1 这类数据集常见的字段结构、标签定义与采样偏差

拿到数据集后,我建议不要急着跑模型,先把文件解压、用.info().head()摸清结构。心理状态识别数据集通常由三部分组成:标识列、特征列、标签列。特征列可能是量表分数,比如 PHQ-9、GAD-7、SCL-90,也可能是从可穿戴设备里统计出来的睡眠时长、心率变异性、日均步数。不同来源的数据,预处理方式完全不同。

下面是一份典型的心理状态识别数据集字段清单,我做项目时会按这个思路归档字段。

字段类别示例字段数据类型在建模中的用途
标识列user_id, sample_idint仅用于关联与去重,不进入特征
人口学特征age, gender, work_hoursint/float可入特征,但缺失率高时慎用
量表特征phq9_total, gad7_totalint与标签相关性极高,需防数据泄漏
行为特征sleep_hours, screen_time, exercise_freqfloat独立信息量最高的特征
标签列label: 0/1/2int目标变量,训练前先看分布

标签定义在同类项目里通常有三种做法:一是临床诊断结果,二是量表总分按阈值切分,三是标注人员对文本的人工打标。三种做法的可信度差异很大,尤其是阈值切分出的标签,天然会引入边界噪声。建议先用value_counts(normalize=True)查看类别占比,如果某一类占比超过 85%,后续就必须处理类别不平衡。

2.2 用 pandas 完成缺失值、离群值处理的最小预处理流

小数据集的预处理原则是“少删多补,先看分布再动手”。常见做法是先丢弃缺失率过高的列,再用中位数填充数值列,最后用 IQR 剔除显著离群值。下面这段代码几乎是这类项目的标配。

import pandas as pd import numpy as np df = pd.read_csv("mental_state_dataset.csv") print("原始形状:", df.shape) print("标签分布:\n", df["label"].value_counts(normalize=True)) # 缺失率超过 30% 的列直接丢弃,避免补值引入噪声 drop_cols = df.columns[df.isnull().mean() > 0.3] df = df.drop(columns=drop_cols) # 数值列使用中位数填充,量表数据多为右偏,均值易被极端值带偏 num_cols = df.select_dtypes(include=[np.number]).columns num_cols = [c for c in num_cols if c != "label"] df[num_cols] = df[num_cols].fillna(df[num_cols].median()) # 按 3 倍 IQR 剔除离群值,注意不对 label 列操作 for col in num_cols: q1, q3 = df[col].quantile([0.25, 0.75]) iqr = q3 - q1 lower = q1 - 3 * iqr upper = q3 + 3 * iqr df = df[(df[col] >= lower) & (df[col] <= upper)] print("预处理后形状:", df.shape)

逻辑说明放在这里:缺失率 30% 是一个行业里常用的阈值,超过这个比例后,用任何统计量填充都会让该特征变成“噪声的镜像”。中位数比均值对离群值更稳健,尤其适合心理健康问卷这类天然右偏的数据。3 倍 IQR 的筛选比 1.5 倍更保守,目的是只去掉生理记录异常值,避免把正常情绪波动样本误删。预处理结束后,建议把df.to_pickle("cleaned.pkl")存为二进制格式,后续训练脚本直接从 pickle 读取,比反复读 CSV 快且不会丢失数据类型。

2.3 类别不平衡与特征筛选:心理状态数据最容易踩的两个坑

心理状态数据里,正常群体通常远多于异常群体,直接训练会导致模型把所有样本都预测成多数类。处理办法有三个:类别权重视为最优先,过采样次之,SMOTE 最后。250KB 级别的小数据集不适合 SMOTE,容易放大噪声。

特征筛选上,我一般用互信息而不是方差过滤。原因是量表特征多为低位数的离散分数,方差过滤会把排序信息丢掉,互信息能捕捉非线性关系。下面是可直接用的筛选代码。

from sklearn.feature_selection import mutual_info_classif X = df.drop(columns=["label"]) y = df["label"] # 计算特征与标签的互信息得分 mi_scores = pd.Series( mutual_info_classif(X, y, random_state=42, n_neighbors=3), index=X.columns ).sort_values(ascending=False) # 保留 top20 特征,小数据集下特征越多过拟合风险越大 keep_cols = mi_scores.head(20).index.tolist() print("保留特征:", keep_cols)

n_neighbors是互信息估计的邻域参数,小样本时取 3 能稳定估计结果,样本过千可以调到 5。特征筛选后务必把keep_cols存成列表或 JSON,训练和预测脚本要用同一份特征列表,这是源代码工程化里最容易遗漏的约束。

3. 从基线模型到深度模型:心理状态预测的建模路径

3.1 为什么先跑基线模型,而不是直接上神经网络

我见过太多次一上来就套全景模型的例子,在小数据集上要么训不收敛,要么训出个 90% 准确率但验证曲线严重分离。心理状态识别数据集通常只有几百到几千条样本,结构上接近“宽表”,这种情况下好用的基线模型是逻辑回归、随机森林和 XGBoost,而不是神经网络。基线的意义在于给出参考线:如果深度模型在小样本上的表现还不如随机森林,说明数据量和特征表达不支持复杂模型,问题不在模型结构而在数据侧。

选逻辑回归是为了获得线性可解释的权重,随机森林用来捕捉非线性特征交互,XGBoost 则作为梯度提升的强化版本。三个模型一起对比时,如果三者表现很接近,说明决策边界简单,数据里可能存在标签泄漏;如果随机森林和 XGBoost 明显优于逻辑回归,说明特征交互关系值得深挖。

3.2 用 sklearn 在 40 行内完成训练、验证与效果对比

下面的脚本把三个模型放在同一个框架里,统一采用分层采样和 5 折交叉验证。分层采样能保证每一折里类别比例与全量一致,在心理状态这种不平衡数据里是硬性要求。

from sklearn.model_selection import train_test_split, cross_val_score, StratifiedKFold from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier from xgboost import XGBClassifier from sklearn.metrics import classification_report X = df[keep_cols] y = df["label"] # stratify=y 保证训练/测试集类别分布一致 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, stratify=y, random_state=42 ) # 类别权重解决不平衡,balanced 按样本量自动加权 models = { "lr": LogisticRegression(max_iter=500, class_weight="balanced"), "rf": RandomForestClassifier( n_estimators=200, max_depth=6, class_weight="balanced" ), "xgb": XGBClassifier( n_estimators=200, learning_rate=0.1, max_depth=4, scale_pos_weight=(y_train == 0).sum() / max((y_train == 1).sum(), 1) ) } cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) for name, model in models.items(): # 交叉验证的 F1 宏平均更能反映不平衡数据下的真实效果 scores = cross_val_score(model, X_train, y_train, cv=cv, scoring="f1_macro") print(f"{name}: CV F1 = {scores.mean():.4f} (+/- {scores.std():.4f})") model.fit(X_train, y_train) y_pred = model.predict(X_test) print(classification_report(y_test, y_pred, digits=3))

这里有几个参数值得单独说。max_depth在随机森林和 XGBoost 里都限制为 6 以内,小数据集上深树必过拟合。scale_pos_weight只在 XGBoost 里处理二分类不平衡,如果是三分类就删掉这一行。max_iter=500保证逻辑回归在标准化不充分时也能收敛。评估指标没有用 accuracy,而是用f1_macro,因为它对少数类更敏感——心理状态识别里漏掉一个异常样本的代价远高于误报一次。

如果模型效果不理想,需要回到特征和数据处理本身去看,但在那之前还要做一个重要检查:确认keep_cols里没有与标签完全同源的字段。

3.3 小数据上的深度模型思路:预训练模型与迁移学习

当样本量不足但模态是文本类时,直接从零训练神经网络毫无必要,常见的思路是用预训练模型做迁移。比如让用户输入一段情绪描述或量表作答文本,用中文预训练语言模型提取语义向量,再把向量接到逻辑回归或随机森林上。这就是所谓的“特征后接浅层分类器”,在小数据集上比微调整个大模型更鲁棒。

一个可行的迁移学习流程是:用transformers库加载预训练模型,对每条文本做平均池化,得到 768 维或 1024 维的句向量,然后作为特征输入到 sklearn 分类器。微调预训练模型时,下面几个参数是小样本场景下的推荐起点。

参数名推荐值调整方向
learning_rate2e-5过拟合则降为 1e-5
batch_size8 或 16显存允许就适当增大
num_epochs3 到 5超过 5 轮容易记住噪声
weight_decay0.01正则化,小数据集不能省
max_length128过长文本截断,减少计算量

如果数据集本身就是结构化表格,那么深度模型的优势并不明显。把表格直接塞进自注意力网络里,往往不如随机森林。因此这里不建议强行上 Transformer,这也是我在类似实战项目里反复验证后的结论。

4. 5 个源代码的分工:从训练脚本到预测脚本的工程化

4.1 源代码文件如何拆分:一个最小但完整的工程结构

标题里特意强调了“5 个源代码”,恰好对应了一个完整预测小项目的标准拆分。我做这类项目时,会按职责拆成五个脚本:数据预处理、模型训练、效果评估、单条预测、可视化分析。这样拆分的好处是“数据脚本改了,训练脚本不用动”,预测脚本在任何时候都可以直接加载最新模型。

以下是常见的文件分工与输入输出约定,具体文件名不必照搬,职责边界是这个结构真正有价值的地方。

文件核心职责输入输出
01_preprocess.py数据清洗、特征筛选原始 CSVcleaned.pkl, features.json
02_train.py模型训练与调参cleaned.pklmodel.pkl
03_evaluate.py加载模型计算指标model.pkl + test.csveval_report.json
04_predict.py单条样本或批量预测用户输入/CSV状态标签与置信度
05_visualize.py特征分布与模型结果可视化cleaned.pkl + 预测结果PNG 图表

features.json是容易被忽略的中间产物。它保存了特征筛选后保留的字段名和顺序,预测脚本必须按同样的列顺序构造输入,否则model.predict()会因为列名错位而输出错误结果。

4.2 训练脚本的参数设计与模型保存约定

训练脚本的硬编码是工程化的天敌。我一般用argparse把关键变量抽成命令行参数,同时提供默认值,这样既能跑python 02_train.py快速验证,也能用--model xgb --cv 5覆盖默认配置。

# 02_train.py 片段 import argparse import joblib import pandas as pd from sklearn.ensemble import RandomForestClassifier from xgboost import XGBClassifier parser = argparse.ArgumentParser(description="心理状态识别训练") parser.add_argument("--data", default="cleaned.pkl") parser.add_argument("--model", default="rf", choices=["lr", "rf", "xgb"]) parser.add_argument("--cv", type=int, default=5) parser.add_argument("--seed", type=int, default=42) parser.add_argument("--output", default="model.pkl") args = parser.parse_args() df = pd.read_pickle(args.data) X = df[[c for c in df.columns if c != "label"]] y = df["label"] if args.model == "rf": model = RandomForestClassifier( n_estimators=200, max_depth=6, class_weight="balanced", random_state=args.seed ) elif args.model == "xgb": model = XGBClassifier(n_estimators=200, learning_rate=0.1, random_state=args.seed) else: from sklearn.linear_model import LogisticRegression model = LogisticRegression(max_iter=500, class_weight="balanced") model.fit(X, y) joblib.dump(model, args.output) print(f"模型已保存至 {args.output}")

--seed参数值得单独说明。随机种子不是摆设,两次训练结果完全一致的前提是设置了同一个随机种子。--cv参数控制交叉验证折数,如果数据量少于 200 条,建议把--cv改为 3,因为 5 折会让每一折的验证集太小,评估波动会掩盖真实效果。这里把类别不平衡的问题放在内部再用class_weight解决一次,而不是只依赖之前的scale_pos_weight

4.3 预测脚本的输入输出约定与批量预测实现

预测脚本是 5 个源代码里最需要贴进业务的部分。它的核心逻辑包含三步:加载模型、按特征列表构造输入、输出标签与置信度。批量预测时,逐行调用更稳妥,避免模型一次性吃入大量畸形数据结构后崩溃。

# 04_predict.py 片段 import json import joblib import pandas as pd def predict_batch(input_csv, model_path="model.pkl", features_json="features.json"): # 特征文件保证预测时的列顺序与训练一致 with open(features_json, "r", encoding="utf-8") as f: keep_cols = json.load(f) model = joblib.load(model_path) df = pd.read_csv(input_csv) # 只保留训练时用过的特征列,多余列直接忽略 X = df[keep_cols] # 输出概率矩阵与最终类别 proba = model.predict_proba(X) preds = model.classes_[proba.argmax(axis=1)] confidence = proba.max(axis=1) # 拼接结果并保存 result = df[["user_id"]].copy() result["pred_label"] = preds result["confidence"] = confidence.round(4) result.to_csv("prediction_result.csv", index=False) return result if __name__ == "__main__": predict_batch("new_users.csv")

这里的关键约束在X = df[keep_cols]。它要求输入的 CSV 必须包含与训练时相同的特征列。如果新数据缺列,会直接抛KeyError,这比静默填充成 NaN 更安全。model.classes_的顺序在训练后是固定的,不能用proba.argmax()的下标直接当标签值,必须通过它映射回真实类别,这一步写错会导致标签错位。

5. 评估、误判边界与部署:把准确率之外的事做对

5.1 用混淆矩阵、F1 和 AUC 一起判断模型有没有真的可用

准确率在心理状态识别里几乎不具备参考价值。假设数据里 90% 是正常样本,模型全预测正常也能拿到 90% 准确率,但这毫无意义。可靠的验证姿势是同时看三样东西:混淆矩阵看错在哪里,F1 宏平均看少数类的召回率,ROC AUC 看排序能力是否稳定。如果 F1 宏平均能到 0.7 以上,且少数类召回率高于 0.6,这个模型才有资格进入试运行阶段。

混淆矩阵的四个格子对应四种业务代价:漏诊比误报代价更高,所以要重点看少数类那一行的召回率。如果召回率低,调整手段是加大少数类的类别权重,或者降低分类阈值。predict_proba输出的概率是第一手信息,分类阈值默认是 0.5,但可以按业务需要调到 0.4 或 0.3。

5.2 数据泄漏是最隐蔽的坑:量表的题目不能既当特征又当答案

在心理状态识别项目中,我踩过的最大一个坑是量表总分与标签同源。当标签是把另一张相关量表按阈值切分出来的,而特征里又包含了这张表或高度相关的重复题目时,模型学到的其实是“总分大于某值即异常”的映射规则,根本不是泛化能力。皮肤接触点在于特征列名很容易骗人,看逻辑不看名字。

一个快速的排查方案是:训练后用pd.Series(model.feature_importances_, index=keep_cols).nlargest(5)输出最重要的前 5 个特征。如果其中出现与标签计算方式高度相关的字段,就要立即反问:这个字段在新样本里能实时拿到吗?如果只有事后才有,那它就是泄漏源。预测脚本上线时,要确保只使用能实时获取的特征。

5.3 用 joblib 与 FastAPI 把预测做成一个简单的接口

模型的最终价值在于对外提供预测能力。小规模落地其实不需要重型推理框架,把 5 个源码头尾的04_predict.py包一层 FastAPI 就够了,这个方案成本最低。下面是部署层的最小实现。

# api.py 片段 from fastapi import FastAPI from pydantic import BaseModel import json, joblib, pandas as pd app = FastAPI() with open("features.json") as f: keep_cols = json.load(f) model = joblib.load("model.pkl") class Sample(BaseModel): user_id: int features: dict @app.post("/predict") def predict(sample: Sample): # 按训练时的特征列顺序构造 DataFrame row = {col: sample.features.get(col) for col in keep_cols} X = pd.DataFrame([row])[keep_cols] proba = model.predict_proba(X)[0] label = int(model.classes_[proba.argmax()]) return { "user_id": sample.user_id, "pred_label": label, "confidence": round(float(proba.max()), 4) }

接口返回的pred_labelconfidence已经能支撑 Web 端展示和告警场景。如果把模型文件放在小批量后台服务里,只需要调用uvicorn api:app --host 0.0.0.0 --port 8000就能对外提供预测能力。整个部署链路里,模型文件大小和推理延迟都不会成为瓶颈,心理状态识别项目验证的最终标准只有一个:模型的稳定性和可解释性,而不是它长了多么复杂的结构。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询