线性回归实战(一):房价预测数据集入库 KingbaseES ------ 表结构设计
------别让"脏数据"毁掉你精心调优的模型
大家好,我是那个总在模型上线前夜发现"面积字段有负值"、又在 KES 里追查一条异常样本血缘的老架构。今天不讲算法,也不谈梯度------我们回到 AI 工程最朴素也最关键的起点:
数据怎么存?
很多人以为:"不就是建个表,把 CSV 导进去吗?"
但现实是:90% 的模型失败,源于糟糕的数据建模 。
特征缺失、单位混乱、版本错乱、标签泄露......这些问题,不是训练时能补救的,必须在入库那一刻就堵住。
而真相是:数据库表结构,就是你的第一层特征工程。
今天我们就以经典的"房价预测"任务为例,手把手设计一套面向机器学习的 KingbaseES 表结构 ,并用 Java 完成数据入库。全程基于电科金仓 KES,不依赖外部工具,只为构建一个干净、可追溯、可复现的 AI 数据底座。
一、原始数据长什么样?
我们采用 Kaggle 上经典的 California Housing Dataset(简化版),包含以下字段:
| 字段 | 含义 | 示例值 |
|---|---|---|
longitude |
经度 | -122.23 |
latitude |
纬度 | 37.88 |
housing_median_age |
房屋中位年龄(年) | 41 |
total_rooms |
总房间数 | 880 |
total_bedrooms |
总卧室数 | 129 |
population |
人口 | 322 |
households |
家庭数 | 126 |
median_income |
收入中位数(万美元) | 8.3252 |
median_house_value |
房价中位数(万美元) | 452600 ← 标签 |
注意:所有数值都是连续型,无分类变量,非常适合线性回归入门。
二、表结构设计原则:为 ML 而生
在传统业务系统中,表设计关注范式、事务、一致性。
但在 AI 场景下,我们要额外考虑:
✅ 特征完整性 :不能丢字段
✅ 数值精度 :避免 float 舍入误差
✅ 空值策略 :明确缺失处理方式
✅ 版本隔离 :支持多轮实验
✅ 标签安全:防止训练时信息泄露
基于此,我们设计三层表结构:
raw/ → 原始数据(只读)
cleaned/ → 清洗后特征(带元数据)
datasets/ → 划分后的训练/测试集(带版本)
三、Step 1:原始数据表(ai_raw.california_housing_raw)
sql
CREATE SCHEMA IF NOT EXISTS ai_raw;
CREATE TABLE ai_raw.california_housing_raw (
id SERIAL PRIMARY KEY,
longitude REAL NOT NULL,
latitude REAL NOT NULL,
housing_median_age REAL NOT NULL,
total_rooms INTEGER NOT NULL,
total_bedrooms INTEGER, -- 允许 NULL(真实场景常见)
population INTEGER NOT NULL,
households INTEGER NOT NULL,
median_income REAL NOT NULL,
median_house_value REAL NOT NULL, -- 标签
source_file VARCHAR(64) NOT NULL DEFAULT 'sklearn_v1',
ingest_time TIMESTAMP DEFAULT NOW()
);
💡 设计说明:
total_bedrooms允许 NULL:模拟真实缺失;- 所有数值用
REAL(4 字节 float):平衡精度与存储;- 加
source_file和ingest_time:追踪数据来源;- 不做任何清洗:保留原始状态,便于回溯。
四、Step 2:清洗后特征表(ai_features.california_housing_cleaned)
清洗逻辑用 SQL 视图实现(可复用、可审计):
sql
CREATE SCHEMA IF NOT EXISTS ai_features;
CREATE OR REPLACE VIEW ai_features.v_california_housing_cleaned AS
SELECT
id,
longitude,
latitude,
housing_median_age,
total_rooms,
COALESCE(total_bedrooms, total_rooms * 0.3) AS total_bedrooms, -- 缺失填充
population,
households,
median_income,
median_house_value,
-- 衍生特征(简单示例)
(total_rooms::REAL / households) AS rooms_per_household,
(population::REAL / households) AS population_per_household,
-- 标签归一化(可选,也可在 Java 中做)
median_house_value / 100000.0 AS label_normalized
FROM ai_raw.california_housing_raw
WHERE
total_rooms > 0
AND households > 0
AND median_income BETWEEN 0.5 AND 15.0; -- 过滤异常值
✅ 优势:
- 清洗逻辑与代码解耦;
- 业务人员可直接查视图验证;
- 未来新增特征,只需改 SQL。
五、Step 3:训练/测试集划分(带版本)
sql
-- 创建版本化表
CREATE TABLE ai_datasets.california_housing_v1_train (
LIKE ai_features.v_california_housing_cleaned INCLUDING ALL
);
CREATE TABLE ai_datasets.california_housing_v1_test (
LIKE ai_features.v_california_housing_cleaned INCLUDING ALL
);
-- 按 ID 哈希划分(确保可复现)
INSERT INTO ai_datasets.california_housing_v1_train
SELECT * FROM ai_features.v_california_housing_cleaned
WHERE ABS(hashtext(id::TEXT)) % 10 < 8; -- 80% 训练
INSERT INTO ai_datasets.california_housing_v1_test
SELECT * FROM ai_features.v_california_housing_cleaned
WHERE ABS(hashtext(id::TEXT)) % 10 >= 8; -- 20% 测试
同时记录元数据:
sql
CREATE TABLE ai_datasets.dataset_version_meta (
version VARCHAR(32) PRIMARY KEY,
description TEXT,
train_count INT,
test_count INT,
created_at TIMESTAMP DEFAULT NOW()
);
INSERT INTO ai_datasets.dataset_version_meta
(version, description, train_count, test_count)
VALUES (
'california_housing_v1',
'Baseline for linear regression, with basic imputation',
(SELECT COUNT(*) FROM ai_datasets.california_housing_v1_train),
(SELECT COUNT(*) FROM ai_datasets.california_housing_v1_test)
);
六、Java 实现:从 CSV 到 KES 入库
java
public void ingestHousingData(String csvPath) throws Exception {
// 1. 读取 CSV(略去解析细节)
List<HousingRecord> records = parseCSV(csvPath);
// 2. 批量插入 raw 表
String sql = """
INSERT INTO ai_raw.california_housing_raw (
longitude, latitude, housing_median_age,
total_rooms, total_bedrooms, population,
households, median_income, median_house_value
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
""";
try (Connection conn = KESDataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
for (HousingRecord r : records) {
ps.setDouble(1, r.longitude);
ps.setDouble(2, r.latitude);
ps.setDouble(3, r.housingMedianAge);
ps.setInt(4, r.totalRooms);
ps.setObject(5, r.totalBedrooms); // null-safe
ps.setInt(6, r.population);
ps.setInt(7, r.households);
ps.setDouble(8, r.medianIncome);
ps.setDouble(9, r.medianHouseValue);
ps.addBatch();
}
ps.executeBatch();
}
System.out.println("Ingested " + records.size() + " records to KES.");
}
🔗 JDBC 驱动请从 电科金仓官网下载,确保支持
setObject处理 NULL。
七、为什么这套设计值得借鉴?
- 可追溯:从 raw → cleaned → dataset,每一步可审计;
- 可复现:哈希划分 + 版本表,确保实验一致;
- 可扩展:新增特征只需改视图,不影响上游;
- 国产友好:完全基于 KES 能力,无需 Python pandas;
- 安全隔离:训练集不含未来信息(如未发生交易)。
结语:数据建模,是 AI 工程的"第一道防线"
在国产化 AI 落地中,我们常被要求"快速出效果"。
但真正的专业,体现在愿意花时间把数据底座打牢。
当你能在电科金仓的 KES 中,用清晰的 schema、可复现的划分、可解释的清洗,构建一个干净的房价数据集------你就已经赢了 80% 的团队。
因为接下来,无论是最小二乘法、梯度下降,还是 DL4J 的神经网络,它们面对的,将是一个值得信赖的世界。
------ 一位相信"好的模型,始于干净的数据"的架构师