跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
JavaAIjava算法

线性回归实战:Java 连接 KingbaseES 进行模型训练与评估

介绍如何使用 Java 结合 KingbaseES 数据库实现线性回归模型的训练与评估。内容包括从数据库加载清洗后的房价数据,进行特征归一化处理,通过手写梯度下降算法训练模型,并计算 R²、MAE、RMSE 等核心指标。此外,还展示了如何将残差写回数据库进行分析,构建端到端的工程化流程,确保模型的可解释性与可信度。

信号故障发布于 2026/3/16更新于 2026/10/2588 浏览

线性回归实战:Java 连接 KingbaseES 进行模型训练与评估

数据入库只是开始。真正的考验是:能否从这张表里,训练出一个不仅'能跑',而且'可信'的模型?

很多人一上来就调 DL4J 的 MultiLayerNetwork,跑完看个 loss 下降就收工。但现实是:loss 低 ≠ 预测准,尤其在线性回归这种可解释模型中,评估比训练更重要。

而真相是:模型的价值,不在于它多复杂,而在于你能多清楚地知道它在哪有效、在哪失效。

今天我们就用纯 Java,从 KES 中加载清洗后的房价数据,手写梯度下降训练线性回归模型,并做一套工程级的评估流程——全程不依赖 Python,不导出 CSV,只用 JDK + KES JDBC + 基础数学。

一、目标明确:我们要评估什么?

对于回归任务,不能只看'平均误差'。我们需要回答:

  • 模型整体拟合程度如何? → R²(决定系数)
  • 预测偏差有多大? → MAE(平均绝对误差)、RMSE(均方根误差)
  • 是否系统性高估或低估? → 残差分布(Residual Plot)
  • 在哪些区域表现差? → 按收入/区域分组分析

这些指标,必须基于测试集计算,且数据完全来自 KES。

二、Java 实现:从 KES 加载训练/测试集

首先定义数据结构:

public static class HousingSample {
    public final double[] features; // [longitude, latitude, ..., rooms_per_household]
    public final double label; // median_house_value (万美元)

    public HousingSample(double[] features, double label) {
        this.features = features;
        this.label = label;
    }
}

然后从 KES 读取:

public List<HousingSample> loadDataset(Connection conn, String tableName) throws SQLException {
    String sql = """
        SELECT longitude, latitude, housing_median_age, total_rooms, total_bedrooms,
               population, households, median_income, rooms_per_household,
               population_per_household, median_house_value AS label
        FROM ai_datasets.""" + tableName;
    List<HousingSample> samples = new ArrayList<>();
    try (PreparedStatement ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery()) {
        while (rs.next()) {
            double[] x = {
                rs.getDouble("longitude"), rs.getDouble("latitude"),
                rs.getDouble("housing_median_age"), rs.getDouble("total_rooms"),
                rs.getDouble("total_bedrooms"), rs.getDouble("population"),
                rs.getDouble("households"), rs.getDouble("median_income"),
                rs.getDouble("rooms_per_household"), rs.getDouble("population_per_household")
            };
            double y = rs.getDouble("label");
            samples.add(new HousingSample(x, y));
        }
    }
    return samples;
}

注意使用最新版 KES JDBC 驱动,以高效读取 REAL 类型。

三、特征归一化:训练前的必要步骤

线性回归对量纲敏感,必须标准化:

public static class FeatureStats {
    public final double[] mean;
    public final double[] std;
}

public static FeatureStats normalize(List<HousingSample> data) {
    int n = data.size();
    int dim = data.get(0).features.length;
    double[] mean = new double[dim];
    double[] std = new double[dim];

    // 计算均值
    for (HousingSample s : data) {
        for (int i = 0; i < dim; i++) {
            mean[i] += s.features[i];
        }
    }
    for (int i = 0; i < dim; i++) mean[i] /= n;

    // 计算标准差
    for (HousingSample s : data) {
        for (int i = 0; i < dim; i++) {
            std[i] += Math.pow(s.features[i] - mean[i], 2);
        }
    }
    for (int i = 0; i < dim; i++) std[i] = Math.sqrt(std[i] / n);

    // 标准化
    for (HousingSample s : data) {
        for (int i = 0; i < dim; i++) {
            s.features[i] = (s.features[i] - mean[i]) / (std[i] == 0 ? 1 : std[i]);
        }
    }
    return new FeatureStats(mean, std);
}

注意:测试集必须用训练集的 mean/std 归一化,避免信息泄露!

四、模型训练:梯度下降实现(回顾)

沿用上一期的手写梯度下降:

public static double[] trainLinearRegression(List<HousingSample> trainData, double lr, int epochs) {
    int n = trainData.size();
    int dim = trainData.get(0).features.length;
    double[] w = new double[dim]; // 权重
    double b = 0.0; // 偏置

    for (int epoch = 0; epoch < epochs; epoch++) {
        double dwSum = 0, dbSum = 0;
        for (int i = 0; i < dim; i++) dwSum = 0; // reset per feature
        for (HousingSample s : trainData) {
            double pred = b;
            for (int i = 0; i < dim; i++) {
                pred += w[i] * s.features[i];
            }
            double error = pred - s.label;
            for (int i = 0; i < dim; i++) {
                w[i] -= lr * (error * s.features[i]) / n;
            }
            b -= lr * error / n;
        }
        if (epoch % 500 == 0) {
            double loss = computeMSE(trainData, w, b);
            System.out.printf("Epoch %d, MSE: %.2f%n", epoch, loss);
        }
    }

    // 合并返回 [w0, w1, ..., b]
    double[] params = new double[dim + 1];
    System.arraycopy(w, 0, params, 0, dim);
    params[dim] = b;
    return params;
}

五、模型评估:不止一个数字

5.1 计算核心指标
public static RegressionMetrics evaluate(List<HousingSample> testData, double[] weights, double bias) {
    int dim = weights.length;
    double totalError = 0, totalAbsError = 0, totalSquaredError = 0;
    double meanY = testData.stream().mapToDouble(s -> s.label).average().orElse(0);
    List<Double> residuals = new ArrayList<>();

    for (HousingSample s : testData) {
        double pred = bias;
        for (int i = 0; i < dim; i++) {
            pred += weights[i] * s.features[i];
        }
        double error = s.label - pred;
        residuals.add(error);
        totalError += error;
        totalAbsError += Math.abs(error);
        totalSquaredError += error * error;
    }

    int m = testData.size();
    double mae = totalAbsError / m;
    double rmse = Math.sqrt(totalSquaredError / m);
    double r2 = 1 - (totalSquaredError / testData.stream().mapToDouble(s -> Math.pow(s.label - meanY, 2)).sum());

    return new RegressionMetrics(mae, rmse, r2, residuals);
}

public static class RegressionMetrics {
    public final double mae, rmse, r2;
    public final List<Double> residuals;

    public RegressionMetrics(double mae, double rmse, double r2, List<Double> residuals) {
        this.mae = mae;
        this.rmse = rmse;
        this.r2 = r2;
        this.residuals = residuals;
    }
}

典型输出:

MAE: 42,300 美元 RMSE: 68,500 美元 R²: 0.61

解读:R²=0.61 表示 61% 的房价波动可由这 10 个特征解释;MAE≈4.2 万,意味着平均预测偏差约 4 万美元——在加州房价中属于可接受范围。

5.2 残差分析:发现模型盲区

我们将残差写回 KES,供后续分析:

public void saveResidualsToKES(Connection conn, List<HousingSample> testData, List<Double> residuals, String version) throws SQLException {
    String sql = """
        INSERT INTO ai_eval.housing_residuals_v1 (sample_id, residual, predicted, actual) VALUES (?, ?, ?, ?)
        """;
    // 假设 HousingSample 包含 id(实际需从 KES 读取)
    try (PreparedStatement ps = conn.prepareStatement(sql)) {
        for (int i = 0; i < testData.size(); i++) {
            ps.setLong(1, testData.get(i).id);
            ps.setDouble(2, residuals.get(i));
            ps.setDouble(3, testData.get(i).predictedValue); // 需补充此字段
            ps.setDouble(4, testData.get(i).label);
            ps.addBatch();
        }
        ps.executeBatch();
    }
}

然后在 KES 中分析:

-- 高收入区域是否预测更准?
SELECT CASE WHEN median_income > 8 THEN 'high' ELSE 'low' END AS income_group,
       AVG(ABS(residual)) AS avg_abs_error
FROM ai_eval.housing_residuals_v1 r
JOIN ai_datasets.california_housing_v1_test t ON r.sample_id = t.id
GROUP BY income_group;

结果可能显示:模型在高收入区域误差更大——提示我们需要引入非线性特征(如 income²)。

六、与 KES 深度协同:构建闭环

整个流程封装为可调度任务:

public void trainAndEvaluateHousingModel() {
    try (Connection conn = KESDataSource.getConnection()) {
        // 1. 加载数据
        var trainData = loadDataset(conn, "california_housing_v1_train");
        var testData = loadDataset(conn, "california_housing_v1_test");

        // 2. 归一化(仅用训练集统计量)
        var stats = normalize(trainData);
        normalizeWithStats(testData, stats); // 复用 train 的 mean/std

        // 3. 训练
        double[] params = trainLinearRegression(trainData, 0.1, 2000);

        // 4. 评估
        double[] w = Arrays.copyOf(params, params.length - 1);
        double b = params[params.length - 1];
        var metrics = evaluate(testData, w, b);

        // 5. 写回结果
        saveModelParamsToKES(conn, "housing_linear_v1", params, stats);
        saveEvaluationMetricsToKES(conn, "housing_linear_v1", metrics);
        saveResidualsToKES(conn, testData, metrics.residuals, "v1");

        System.out.printf("✅ Training complete. R²=%.3f, MAE=%.0f\n", metrics.r2, metrics.mae);
    }
}

结语:评估,是模型走向生产的通行证

在 AI 工程中,训练只是开始,评估才是信任的起点。

今天我们用 Java + KingbaseES,完成了一次端到端的线性回归实战:

  • 从版本化表加载数据;
  • 手写梯度下降训练;
  • 计算多维评估指标;
  • 将残差写回数据库供分析。

这套流程具备企业级的可审计性与可解释性。

当你能向业务方展示:'我们的模型在高收入区域误差偏大,建议下一轮加入平方项',你就不再是'调包侠',而是值得信赖的 AI 架构师。

目录

  1. 线性回归实战:Java 连接 KingbaseES 进行模型训练与评估
  2. 一、目标明确:我们要评估什么?
  3. 二、Java 实现:从 KES 加载训练/测试集
  4. 三、特征归一化:训练前的必要步骤
  5. 四、模型训练:梯度下降实现(回顾)
  6. 五、模型评估:不止一个数字
  7. 5.1 计算核心指标
  8. 5.2 残差分析:发现模型盲区
  9. 六、与 KES 深度协同:构建闭环
  10. 结语:评估,是模型走向生产的通行证

更多推荐文章

查看全部
  • 无成本安装配置 OpenClaw 龙虾 AI 全能助手
  • Mac mini M4 部署 OpenClaw + Ollama 本地大模型接入飞书机器人
  • 如何提升自身 WEB 渗透能力?
  • ICML 2024 顶级论文:机器学习领域新进展
  • 基于 Nexent 构建 AI 智能体实现工作文档智能管理
  • Agent Symbolic Learning:首个实现 AI 自主进化的端到端符号化训练框架
  • 第十六届极客大挑战 Web 安全题目复现与解析
  • 飞书 OpenClaw 机器人 HTTP 401 认证失败排查与解决
  • 微信指挥 AI 员工:QClaw 本地部署与实战指南
  • AI Agent 生产级框架实战:架构设计与核心代码实现
  • AI 编程工具对比:GitHub Copilot、Trae 与 Cursor
  • Python 数据分析相比 Excel 的优势与入门指南
  • 滑动窗口四题的共通解法
  • LazyLLM 多 Agent 应用实战:源码部署与 Web 调试全链路
  • VS Code 前端开发必备插件指南:10 款效率工具与配置详解
  • 腾讯混元大模型业务落地实践与技术方案
  • AI 写作实战项目:自动写作助手设计与实现
  • C++ 继承机制详解:栈实现、名称隐藏与默认成员函数
  • Apache 2.2 部署 ASP.NET 3.5 实战配置指南
  • LLaMA 网络架构深度解析

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online