Java机器学习实战从零构建智能推荐系统与预测模型代码全解析

引言
在当今数据驱动的时代,机器学习已经成为智能应用的核心技术。虽然Python在机器学习领域占据主导地位,但Java作为企业级应用的首选语言,在构建高性能、可扩展的机器学习系统方面具有独特优势。本文将详细介绍如何使用Java从零开始构建智能推荐系统和预测模型,并提供完整的代码实现和详细解析。
一、环境准备与项目搭建
1.1 开发环境配置
首先,我们需要配置Java开发环境和相关依赖。推荐使用Maven作为项目管理工具。

4.0.0

com.example
ml-java-recommender
1.0.0

11
11 UTF-8




org.apache.commons
commons-math3
3.6.1



nz.ac.waikato.cms.weka
weka-stable
3.8.6



com.github.haifengl
smile-core
3.0.1



com.google.code.gson
gson
2.10.1

1.2 项目结构设计
合理的项目结构对于机器学习项目至关重要:
src/main/java/com/example/ml/
├── data/ # 数据处理模块
│ ├── DataPreprocessor.java
│ ├── DataLoader.java
│ └── FeatureExtractor.java
├── models/ # 机器学习模型
│ ├── recommendation/ # 推荐系统
│ │ ├── CollaborativeFiltering.java
│ │ ├── ContentBasedRecommender.java
│ │ └── HybridRecommender.java
│ └── prediction/ # 预测模型
│ ├── LinearRegression.java
│ ├── DecisionTree.java
│ └── RandomForest.java
├── evaluation/ # 模型评估
│ ├── Evaluator.java
│ └── Metrics.java
└── utils/ # 工具类
├── MathUtils.java
└── FileIO.java
二、数据处理与特征工程
2.1 数据加载与预处理
在机器学习项目中,数据处理是第一步。我们需要创建一个灵活的数据加载器来处理不同格式的数据。
// src/main/java/com/example/ml/data/DataLoader.java
package com.example.ml.data;

import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

/**
* 数据加载器:负责从各种数据源加载和预处理数据
*/
public class DataLoader {

/**
* 从CSV文件加载用户-物品评分数据
* @param filePath CSV文件路径
* @return 评分数据列表,每个元素为[userID, itemID, rating]
*/
public static List loadRatingsFromCSV(String filePath) throws IOException {
List ratings = new ArrayList<>();

try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
String line;
// 跳过标题行
br.readLine();

while ((line = br.readLine()) != null) {
String[] values = line.split(“,”);
if (values.length >= 3) {
double userId = Double.parseDouble(values[0].trim());
double itemId = Double.parseDouble(values[1].trim());
double rating = Double.parseDouble(values[2].trim());
ratings.add(new double[]{userId, itemId, rating});
}
}
}

return ratings;
}

/**
* 将评分数据转换为用户-物品矩阵
* @param ratings 评分数据列表
* @param userCount 用户数量
* @param itemCount 物品数量
* @return 用户-物品评分矩阵
*/
public static double[][] convertToUserItemMatrix(List ratings,
int userCount, int itemCount) {
double[][] matrix = new double[userCount][itemCount];

// 初始化矩阵为0(表示未评分)
for (double[] row : matrix) {
Arrays.fill(row, 0.0);
}

// 填充评分数据
for (double[] rating : ratings) {
int userId = (int) rating[0];
int itemId = (int) rating[1];
double score = rating[2];

if (userId < userCount && itemId < itemCount) { matrix[userId][itemId] = score; } } return matrix; } /** * 数据标准化:将评分数据转换为0-1范围 * @param matrix 原始评分矩阵 * @return 标准化后的矩阵 */ public static double[][] normalizeMatrix(double[][] matrix) { int rows = matrix.length; int cols = matrix[0].length; double[][] normalized = new double[rows][cols]; for (int i = 0; i < rows; i++) { double max = Arrays.stream(matrix[i]).max().orElse(0.0); double min = Arrays.stream(matrix[i]).min().orElse(0.0); if (max - min > 0) {
for (int j = 0; j < cols; j++) { normalized[i][j] = (matrix[i][j] - min) / (max - min); } } else { System.arraycopy(matrix[i], 0, normalized[i], 0, cols); } } return normalized; } /** * 数据集划分:将数据分为训练集和测试集 * @param ratings 原始评分数据 * @param trainRatio 训练集比例(0-1) * @return 包含训练集和测试集的Map */ public static Map> splitDataset(List ratings,
double trainRatio) {
Collections.shuffle(ratings); // 随机打乱数据

int splitIndex = (int) (ratings.size() * trainRatio);
List trainSet = ratings.subList(0, splitIndex);
List testSet = ratings.subList(splitIndex, ratings.size());

Map> dataset = new HashMap<>();
dataset.put(“train”, trainSet);
dataset.put(“test”, testSet);

return dataset;
}
}
2.2 特征提取器
// src/main/java/com/example/ml/data/FeatureExtractor.java
package com.example.ml.data;

import java.util.*;

/**
* 特征提取器:从原始数据中提取有用的特征
*/
public class FeatureExtractor {

/**
* 计算用户特征向量(基于评分历史)
* @param userRatings 用户的评分记录
* @param itemFeatures 物品特征向量
* @return 用户特征向量
*/
public static double[] extractUserFeatures(List userRatings,
Map itemFeatures) {
if (userRatings.isEmpty()) {
return new double[0];
}

int featureDim = itemFeatures.values().stream()
.mapToInt(v -> v.length)
.findFirst()
.orElse(0);

double[] userFeature = new double[featureDim];
double totalWeight = 0;

for (double[] rating : userRatings) {
int itemId = (int) rating[1];
double score = rating[2];

if (itemFeatures.containsKey(itemId)) {
double[] itemFeature = itemFeatures.get(itemId);
for (int i = 0; i < featureDim; i++) { userFeature[i] += itemFeature[i] * score; totalWeight += score; } } } // 归一化 if (totalWeight > 0) {
for (int i = 0; i < featureDim; i++) { userFeature[i] /= totalWeight; } } return userFeature; } /** * 提取物品特征向量(基于TF-IDF或One-Hot编码) * @param itemMetadata 物品元数据(如类别、标签等) * @return 物品特征映射 */ public static Map extractItemFeatures(
Map> itemMetadata) {

// 收集所有可能的特征维度
Set allFeatures = new HashSet<>();
for (Map metadata : itemMetadata.values()) {
allFeatures.addAll(metadata.keySet());
}

// 建立特征索引映射
List featureList = new ArrayList<>(allFeatures);
Map featureIndex = new HashMap<>();
for (int i = 0; i < featureList.size(); i++) { featureIndex.put(featureList.get(i), i); } // 构建特征向量 Map itemFeatures = new HashMap<>();
for (Map.Entry> entry : itemMetadata.entrySet()) {
int itemId = entry.getKey();
Map metadata = entry.getValue();

double[] featureVector = new double[featureList.size()];
Arrays.fill(featureVector, 0.0);

for (Map.Entry metaEntry : metadata.entrySet()) {
String feature = metaEntry.getKey();
Object value = metaEntry.getValue();

if (featureIndex.containsKey(feature)) {
int idx = featureIndex.get(feature);
// 简单的One-Hot编码,实际应用中可以使用更复杂的编码方式
if (value instanceof Boolean) {
featureVector[idx] = (Boolean) value ? 1.0 : 0.0;
} else if (value instanceof Number) {
featureVector[idx] = ((Number) value).doubleValue();
} else {
featureVector[idx] = 1.0; // 存在即为1
}
}
}

itemFeatures.put(itemId, featureVector);
}

return itemFeatures;
}

/**
* 计算余弦相似度
* @param vector1 特征向量1
* @param vector2 特征向量2
* @return 相似度分数
*/
public static double cosineSimilarity(double[] vector1, double[] vector2) {
if (vector1.length != vector2.length) {
return 0.0;
}

double dotProduct = 0.0;
double norm1 = 0.0;
double norm2 = 0.0;

for (int i = 0; i < vector1.length; i++) { dotProduct += vector1[i] * vector2[i]; norm1 += vector1[i] * vector1[i]; norm2 += vector2[i] * vector2[i]; } if (norm1 == 0 || norm2 == 0) { return 0.0; } return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2)); } } 三、推荐系统实现 3.1 协同过滤推荐算法 协同过滤是推荐系统中最经典的方法,包括基于用户的协同过滤和基于物品的协同过滤。 // src/main/java/com/example/ml/models/recommendation/CollaborativeFiltering.java package com.example.ml.models.recommendation; import com.example.ml.data.DataLoader; import com.example.ml.data.FeatureExtractor; import java.util.*; import java.util.stream.Collectors; /** * 协同过滤推荐器 * 支持基于用户和基于物品的协同过滤 */ public class CollaborativeFiltering { private double[][] userItemMatrix; private int userCount; private int itemCount; private String similarityMethod; // "cosine" or "pearson" /** * 构造函数 * @param userItemMatrix 用户-物品评分矩阵 * @param similarityMethod 相似度计算方法 */ public CollaborativeFiltering(double[][] userItemMatrix, String similarityMethod) { this.userItemMatrix = userItemMatrix; this.userCount = userItemMatrix.length; this.itemCount = userItemMatrix[0].length; this.similarityMethod = similarityMethod; } /** * 计算两个用户之间的相似度 * @param user1 用户1的ID * @param user2 用户2的ID * @return 相似度分数 */ private double calculateUserSimilarity(int user1, int user2) { if (similarityMethod.equals("cosine")) { return cosineSimilarity(userItemMatrix[user1], userItemMatrix[user2]); } else { return pearsonCorrelation(userItemMatrix[user1], userItemMatrix[user2]); } } /** * 余弦相似度计算 */ private double cosineSimilarity(double[] vec1, double[] vec2) { double dotProduct = 0.0; double norm1 = 0.0; double norm2 = 0.0; for (int i = 0; i < vec1.length; i++) { // 只考虑共同评分的物品 if (vec1[i] > 0 && vec2[i] > 0) {
dotProduct += vec1[i] * vec2[i];
norm1 += vec1[i] * vec1[i];
norm2 += vec2[i] * vec2[i];
}
}

if (norm1 == 0 || norm2 == 0) {
return 0.0;
}

return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
}

/**
* 皮尔逊相关系数计算
*/
private double pearsonCorrelation(double[] vec1, double[] vec2) {
double sum1 = 0.0, sum2 = 0.0;
double sum1Sq = 0.0, sum2Sq = 0.0;
double pSum = 0.0;
int n = 0;

for (int i = 0; i < vec1.length; i++) { if (vec1[i] > 0 && vec2[i] > 0) {
sum1 += vec1[i];
sum2 += vec2[i];
sum1Sq += vec1[i] * vec1[i];
sum2Sq += vec2[i] * vec2[i];
pSum += vec1[i] * vec2[i];
n++;
}
}

if (n == 0) return 0.0;

double num = pSum – (sum1 * sum2 / n);
double den = Math.sqrt((sum1Sq – sum1 * sum1 / n) * (sum2Sq – sum2 * sum2 / n));

if (den == 0.0) return 0.0;

return num / den;
}

/**
* 基于用户的协同过滤推荐
* @param targetUserId 目标用户ID
* @param topK 推荐数量
* @return 推荐物品列表及预测评分
*/
public List> recommendByUser(int targetUserId, int topK) {
// 找到目标用户未评分的物品
Set unratedItems = new HashSet<>();
for (int j = 0; j < itemCount; j++) { if (userItemMatrix[targetUserId][j] == 0) { unratedItems.add(j); } } // 对每个未评分物品计算预测评分 Map predictions = new HashMap<>();

for (int item : unratedItems) {
double weightedSum = 0.0;
double similaritySum = 0.0;

// 遍历所有其他用户
for (int otherUser = 0; otherUser < userCount; otherUser++) { if (otherUser != targetUserId && userItemMatrix[otherUser][item] > 0) {
double similarity = calculateUserSimilarity(targetUserId, otherUser);
if (similarity > 0) { // 只考虑正相似度
weightedSum += similarity * userItemMatrix[otherUser][item];
similaritySum += similarity;
}
}
}

if (similaritySum > 0) {
predictions.put(item, weightedSum / similaritySum);
}
}

// 返回topK推荐
return predictions.entrySet().stream()
.sorted((e1, e2) -> Double.compare(e2.getValue(), e1.getValue()))
.limit(topK)
.collect(Collectors.toList());
}

/**
* 基于物品的协同过滤推荐
* @param targetUserId 目标用户ID
* @param topK 推荐数量
* @return 推荐物品列表及预测评分
*/
public List> recommendByItem(int targetUserId, int topK) {
// 计算物品相似度矩阵(可以预先计算并缓存)
double[][] itemSimilarity = calculateItemSimilarityMatrix();

// 找到目标用户已评分的物品
Map ratedItems = new HashMap<>();
for (int j = 0; j < itemCount; j++) { if (userItemMatrix[targetUserId][j] > 0) {
ratedItems.put(j, userItemMatrix[targetUserId][j]);
}
}

// 对每个未评分物品计算预测评分
Map predictions = new HashMap<>();
for (int targetItem = 0; targetItem < itemCount; targetItem++) { if (userItemMatrix[targetUserId][targetItem] == 0) { double weightedSum = 0.0; double similaritySum = 0.0; for (Map.Entry entry : ratedItems.entrySet()) {
int ratedItem = entry.getKey();
double rating = entry.getValue();
double similarity = itemSimilarity[targetItem][ratedItem];

if (similarity > 0) {
weightedSum += similarity * rating;
similaritySum += similarity;
}
}

if (similaritySum > 0) {
predictions.put(targetItem, weightedSum / similaritySum);
}
}
}

// 返回topK推荐
return predictions.entrySet().stream()
.sorted((e1, e2) -> Double.compare(e2.getValue(), e1.getValue()))
.limit(topK)
.collect(Collectors.toList());
}

/**
* 计算物品相似度矩阵
*/
private double[][] calculateItemSimilarityMatrix() {
double[][] similarityMatrix = new double[itemCount][itemCount];

for (int i = 0; i < itemCount; i++) { for (int j = 0; j < itemCount; j++) { if (i == j) { similarityMatrix[i][j] = 1.0; } else { // 提取物品i和j的评分向量 double[] vec1 = new double[userCount]; double[] vec2 = new double[userCount]; for (int u = 0; u < userCount; u++) { vec1[u] = userItemMatrix[u][i]; vec2[u] = userItemMatrix[u][j]; } similarityMatrix[i][j] = cosineSimilarity(vec1, vec2); } } } return similarityMatrix; } } 3.2 基于内容的推荐系统 // src/main/java/com/example/ml/models/recommendation/ContentBasedRecommender.java package com.example.ml.models.recommendation; import com.example.ml.data.FeatureExtractor; import java.util.*; import java.util.stream.Collectors; /** * 基于内容的推荐器 * 通过分析物品特征和用户偏好进行推荐 */ public class ContentBasedRecommender { private Map itemFeatures; // 物品特征向量
private Map> userRatings; // 用户评分历史

/**
* 构造函数
* @param itemFeatures 物品特征映射
* @param userRatings 用户评分数据
*/
public ContentBasedRecommender(Map itemFeatures,
Map> userRatings) {
this.itemFeatures = itemFeatures;
this.userRatings = userRatings;
}

/**
* 为用户生成推荐
* @param userId 用户ID
* @param topK 推荐数量
* @return 推荐列表
*/
public List> recommend(int userId, int topK) {
// 获取用户评分历史
List userHistory = userRatings.getOrDefault(userId, new ArrayList<>());

// 构建用户偏好向量
double[] userPreference = buildUserPreferenceVector(userHistory);

// 计算用户与所有物品的相似度
Map scores = new HashMap<>();

for (Map.Entry entry : itemFeatures.entrySet()) {
int itemId = entry.getKey();
double[] itemFeature = entry.getValue();

// 跳过用户已评分的物品
if (userHistory.stream().anyMatch(r -> (int) r[1] == itemId)) {
continue;
}

double similarity = FeatureExtractor.cosineSimilarity(userPreference, itemFeature);
scores.put(itemId, similarity);
}

// 返回topK推荐
return scores.entrySet().stream()
.sorted((e1, e2) -> Double.compare(e2.getValue(), e1.getValue()))
.limit(topK)
.collect(Collectors.toList());
}

/**
* 构建用户偏好向量
* 通过加权平均用户已评分物品的特征向量
*/
private double[] buildUserPreferenceVector(List userRatings) {
if (userRatings.isEmpty()) {
return new double[0];
}

// 确定特征维度
int featureDim = itemFeatures.values().stream()
.mapToInt(v -> v.length)
.findFirst()
.orElse(0);

if (featureDim == 0) {
return new double[0];
}

double[] preference = new double[featureDim];
double totalWeight = 0;

for (double[] rating : userRatings) {
int itemId = (int) rating[1];
double score = rating[2];

if (itemFeatures.containsKey(itemId)) {
double[] itemFeature = itemFeatures.get(itemId);
for (int i = 0; i < featureDim; i++) { preference[i] += itemFeature[i] * score; totalWeight += score; } } } // 归一化 if (totalWeight > 0) {
for (int i = 0; i < featureDim; i++) { preference[i] /= totalWeight; } } return preference; } /** * 更新用户偏好(当用户有新评分时) * @param userId 用户ID * @param itemId 物品ID * @param rating 评分 */ public void updateUserPreference(int userId, int itemId, double rating) { userRatings.computeIfAbsent(userId, k -> new ArrayList<>())
.add(new double[]{userId, itemId, rating});
}
}
3.3 混合推荐系统
// src/main/java/com/example/ml/models/recommendation/HybridRecommender.java
package com.example.ml.models.recommendation;

import java.util.*;
import java.util.stream.Collectors;

/**
* 混合推荐器
* 结合协同过滤和基于内容的推荐
*/
public class HybridRecommender {

private CollaborativeFiltering cf;
private ContentBasedRecommender cb;
private double cfWeight; // 协同过滤权重
private double cbWeight; // 基于内容的权重

/**
* 构造函数
* @param cf 协同过滤推荐器
* @param cb 基于内容的推荐器
* @param cfWeight 协同过滤权重
* @param cbWeight 基于内容的权重
*/
public HybridRecommender(CollaborativeFiltering cf,
ContentBasedRecommender cb,
double cfWeight, double cbWeight) {
this.cf = cf;
this.cb = cb;
this.cfWeight = cfWeight;
this.cbWeight = cbWeight;
}

/**
* 混合推荐
* @param userId 用户ID
* @param topK 推荐数量
* @return 推荐列表
*/
public List> recommend(int userId, int topK) {
// 获取协同过滤推荐
List> cfRecommendations =
cf.recommendByUser(userId, topK * 2); // 获取更多候选

// 获取基于内容的推荐
List> cbRecommendations =
cb.recommend(userId, topK * 2); // 获取更多候选

// 合并并加权评分
Map combinedScores = new HashMap<>();

// 添加协同过滤分数
for (Map.Entry entry : cfRecommendations) {
combinedScores.put(entry.getKey(),
combinedScores.getOrDefault(entry.getKey(), 0.0) +
entry.getValue() * cfWeight);
}

// 添加基于内容的分数
for (Map.Entry entry : cbRecommendations) {
combinedScores.put(entry.getKey(),
combinedScores.getOrDefault(entry.getKey(), 0.0) +
entry.getValue() * cbWeight);
}

// 归一化分数(可选)
double maxScore = combinedScores.values().stream()
.max(Double::compare).orElse(1.0);

if (maxScore > 0) {
for (Integer key : combinedScores.keySet()) {
combinedScores.put(key, combinedScores.get(key) / maxScore);
}
}

// 返回topK
return combinedScores.entrySet().stream()
.sorted((e1, e2) -> Double.compare(e2.getValue(), e1.getValue()))
.limit(topK)
.collect(Collectors.toList());
}
}
四、预测模型实现
4.1 线性回归模型
// src/main/java/com/example/ml/models/prediction/LinearRegression.java
package com.example.ml.models.prediction;

import java.util.Arrays;

/**
* 线性回归模型
* 实现梯度下降法进行训练
*/
public class LinearRegression {

private double[] weights; // 特征权重
private double bias; // 偏置项
private double learningRate; // 学习率
private int maxIterations; // 最大迭代次数
private double tolerance; // 收敛阈值

/**
* 构造函数
*/
public LinearRegression(double learningRate, int maxIterations, double tolerance) {
this.learningRate = learningRate;
this.maxIterations = maxIterations;
this.tolerance = tolerance;
}

/**
* 训练模型
* @param X 训练特征,形状为[n_samples, n_features]
* @param y 目标值,形状为[n_samples]
*/
public void train(double[][] X, double[] y) {
int nSamples = X.length;
int nFeatures = X[0].length;

// 初始化权重
this.weights = new double[nFeatures];
this.bias = 0.0;

// 梯度下降
double prevLoss = Double.MAX_VALUE;

for (int iter = 0; iter < maxIterations; iter++) { double[] predictions = predict(X); // 计算梯度 double[] weightGradients = new double[nFeatures]; double biasGradient = 0.0; for (int i = 0; i < nSamples; i++) { double error = predictions[i] - y[i]; // 更新权重梯度 for (int j = 0; j < nFeatures; j++) { weightGradients[j] += error * X[i][j]; } // 更新偏置梯度 biasGradient += error; } // 平均梯度 for (int j = 0; j < nFeatures; j++) { weightGradients[j] /= nSamples; } biasGradient /= nSamples; // 更新参数 for (int j = 0; j < nFeatures; j++) { weights[j] -= learningRate * weightGradients[j]; } bias -= learningRate * biasGradient; // 计算损失并检查收敛 double currentLoss = computeLoss(X, y); if (Math.abs(prevLoss - currentLoss) < tolerance) { System.out.println("Converged at iteration " + iter); break; } prevLoss = currentLoss; // 每100次迭代打印损失 if (iter % 100 == 0) { System.out.println("Iteration " + iter + ", Loss: " + currentLoss); } } } /** * 预测 * @param X 输入特征 * @return 预测值 */ public double[] predict(double[][] X) { double[] predictions = new double[X.length]; for (int i = 0; i < X.length; i++) { double sum = bias; for (int j = 0; j < weights.length; j++) { sum += X[i][j] * weights[j]; } predictions[i] = sum; } return predictions; } /** * 计算损失(均方误差) */ private double computeLoss(double[][] X, double[] y) { double[] predictions = predict(X); double sumSquaredError = 0.0; for (int i = 0; i < y.length; i++) { double error = predictions[i] - y[i]; sumSquaredError += error * error; } return sumSquaredError / y.length; } /** * 获取模型参数 */ public double[] getWeights() { return weights; } public double getBias() { return bias; } } 4.2 决策树模型 // src/main/java/com/example/ml/models/prediction/DecisionTree.java package com.example.ml.models.prediction; import java.util.*; import java.util.stream.Collectors; /** * 决策树分类器 * 实现CART算法 */ public class DecisionTree { private Node root; // 树的根节点 private int maxDepth; // 最大深度 private int minSamplesSplit; // 分裂所需最小样本数 /** * 决策树节点 */ private static class Node { int featureIndex; // 分裂特征索引 double threshold; // 分裂阈值 double value; // 节点值(叶节点时使用) Node left; // 左子节点 Node right; // 右子节点 boolean isLeaf; // 是否为叶节点 Node() { this.isLeaf = false; this.value = 0.0; } } /** * 构造函数 */ public DecisionTree(int maxDepth, int minSamplesSplit) { this.maxDepth = maxDepth; this.minSamplesSplit = minSamplesSplit; } /** * 训练决策树 * @param X 训练特征 * @param y 目标值 */ public void train(double[][] X, double[] y) { this.root = buildTree(X, y, 0); } /** * 递归构建树 */ private Node buildTree(double[][] X, double[] y, int depth) { Node node = new Node(); // 检查停止条件 if (depth >= maxDepth || X.length < minSamplesSplit || isPure(y)) { node.isLeaf = true; node.value = calculateMean(y); return node; } // 寻找最佳分裂 SplitResult bestSplit = findBestSplit(X, y); if (bestSplit == null || bestSplit.gain == 0) { node.isLeaf = true; node.value = calculateMean(y); return node; } // 设置分裂信息 node.featureIndex = bestSplit.featureIndex; node.threshold = bestSplit.threshold; // 分割数据 SplitData splitData = splitData(X, y, bestSplit.featureIndex, bestSplit.threshold); // 递归构建子树 node.left = buildTree(splitData.leftX, splitData.leftY, depth + 1); node.right = buildTree(splitData.rightX, splitData.rightY, depth + 1); return node; } /** * 寻找最佳分裂点 */ private SplitResult findBestSplit(double[][] X, double[] y) { int nFeatures = X[0].length; SplitResult bestSplit = null; double bestGain = -1; // 遍历所有特征 for (int feature = 0; feature < nFeatures; feature++) { // 获取该特征的所有可能值 Set thresholds = new HashSet<>();
for (double[] sample : X) {
thresholds.add(sample[feature]);
}

// 遍历所有可能的阈值
for (double threshold : thresholds) {
SplitData splitData = splitData(X, y, feature, threshold);

if (splitData.leftY.length == 0 || splitData.rightY.length == 0) {
continue;
}

double gain = calculateInformationGain(y, splitData.leftY, splitData.rightY);

if (gain > bestGain) {
bestGain = gain;
bestSplit = new SplitResult(feature, threshold, gain);
}
}
}

return bestSplit;
}

/**
* 根据特征和阈值分割数据
*/
private SplitData splitData(double[][] X, double[] y, int featureIndex, double threshold) {
List leftXList = new ArrayList<>();
List leftYList = new ArrayList<>();
List rightXList = new ArrayList<>();
List rightYList = new ArrayList<>();

for (int i = 0; i < X.length; i++) { if (X[i][featureIndex] <= threshold) { leftXList.add(X[i]); leftYList.add(y[i]); } else { rightXList.add(X[i]); rightYList.add(y[i]); } } // 转换为数组 double[][] leftX = leftXList.toArray(new double[0][]); double[] leftY = leftYList.stream().mapToDouble(Double::doubleValue).toArray(); double[][] rightX = rightXList.toArray(new double[0][]); double[] rightY = rightYList.stream().mapToDouble(Double::doubleValue).toArray(); return new SplitData(leftX, leftY, rightX, rightY); } /** * 计算信息增益 */ private double calculateInformationGain(double[] parent, double[] left, double[] right) { double parentVariance = calculateVariance(parent); double leftVariance = calculateVariance(left); double rightVariance = calculateVariance(right); double nParent = parent.length; double nLeft = left.length; double nRight = right.length; return parentVariance - (nLeft / nParent) * leftVariance - (nRight / nParent) * rightVariance; } /** * 计算方差 */ private double calculateVariance(double[] values) { if (values.length == 0) return 0; double mean = calculateMean(values); double variance = 0.0; for (double value : values) { variance += (value - mean) * (value - mean); } return variance / values.length; } /** * 计算均值 */ private double calculateMean(double[] values) { if (values.length == 0) return 0; return Arrays.stream(values).average().orElse(0.0); } /** * 检查是否为纯节点 */ private boolean isPure(double[] y) { if (y.length == 0) return true; double first = y[0]; for (double value : y) { if (value != first) return false; } return true; } /** * 预测 */ public double predict(double[] sample) { return predictRecursive(sample, root); } private double predictRecursive(double[] sample, Node node) { if (node.isLeaf) { return node.value; } if (sample[node.featureIndex] <= node.threshold) { return predictRecursive(sample, node.left); } else { return predictRecursive(sample, node.right); } } // 辅助类 private static class SplitResult { int featureIndex; double threshold; double gain; SplitResult(int featureIndex, double threshold, double gain) { this.featureIndex = featureIndex; this.threshold = threshold; this.gain = gain; } } private static class SplitData { double[][] leftX; double[] leftY; double[][] rightX; double[] rightY; SplitData(double[][] leftX, double[] leftY, double[][] rightX, double[] rightY) { this.leftX = leftX; this.leftY = leftY; this.rightX = rightX; this.rightY = rightY; } } } 4.3 随机森林模型 // src/main/java/com/example/ml/models/prediction/RandomForest.java package com.example.ml.models.prediction; import java.util.*; /** * 随机森林模型 * 集成多个决策树 */ public class RandomForest { private List trees;
private int nTrees; // 树的数量
private int maxDepth; // 每棵树的最大深度
private int minSamplesSplit; // 最小分裂样本数
private int maxFeatures; // 每棵树使用的最大特征数

/**
* 构造函数
*/
public RandomForest(int nTrees, int maxDepth, int minSamplesSplit, int maxFeatures) {
this.nTrees = nTrees;
this.maxDepth = maxDepth;
this.minSamplesSplit = minSamplesSplit;
this.maxFeatures = maxFeatures;
this.trees = new ArrayList<>();
}

/**
* 训练随机森林
* @param X 训练特征
* @param y 目标值
*/
public void train(double[][] X, double[] y) {
trees.clear();
Random random = new Random();

for (int i = 0; i < nTrees; i++) { // Bagging: 有放回采样 int nSamples = X.length; double[][] bootstrapX = new double[nSamples][]; double[] bootstrapY = new double[nSamples]; for (int j = 0; j < nSamples; j++) { int idx = random.nextInt(nSamples); bootstrapX[j] = X[idx]; bootstrapY[j] = y[idx]; } // 随机选择特征子集 int nFeatures = X[0].length; int featuresToUse = Math.min(maxFeatures, nFeatures); List featureIndices = new ArrayList<>();
for (int j = 0; j < nFeatures; j++) { featureIndices.add(j); } Collections.shuffle(featureIndices); List selectedFeatures = featureIndices.subList(0, featuresToUse);

// 创建并训练决策树
DecisionTree tree = new DecisionTree(maxDepth, minSamplesSplit);

// 这里简化处理:实际应该限制树只使用选定特征
// 为简化代码,我们直接训练,但在实际应用中需要修改DecisionTree类
tree.train(bootstrapX, bootstrapY);
trees.add(tree);

System.out.println(“Trained tree ” + (i + 1) + “/” + nTrees);
}
}

/**
* 预测(回归)
*/
public double predict(double[] sample) {
double sum = 0.0;
for (DecisionTree tree : trees) {
sum += tree.predict(sample);
}
return sum / trees.size();
}

/**
* 预测(分类)
*/
public int predictClass(double[] sample) {
Map votes = new HashMap<>();

for (DecisionTree tree : trees) {
int prediction = (int) Math.round(tree.predict(sample));
votes.put(prediction, votes.getOrDefault(prediction, 0) + 1);
}

// 返回票数最多的类别
return votes.entrySet().stream()
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElse(0);
}
}
五、模型评估与优化
5.1 评估指标实现
// src/main/java/com/example/ml/evaluation/Metrics.java
package com.example.ml.evaluation;

import java.util.*;

/**
* 模型评估指标
*/
public class Metrics {

/**
* 计算均方误差(MSE)
*/
public static double mse(double[] actual, double[] predicted) {
if (actual.length != predicted.length) {
throw new IllegalArgumentException(“Arrays must have same length”);
}

double sum = 0.0;
for (int i = 0; i < actual.length; i++) { double error = actual[i] - predicted[i]; sum += error * error; } return sum / actual.length; } /** * 计算均方根误差(RMSE) */ public static double rmse(double[] actual, double[] predicted) { return Math.sqrt(mse(actual, predicted)); } /** * 计算平均绝对误差(MAE) */ public static double mae(double[] actual, double[] predicted) { if (actual.length != predicted.length) { throw new IllegalArgumentException("Arrays must have same length"); } double sum = 0.0; for (int i = 0; i < actual.length; i++) { sum += Math.abs(actual[i] - predicted[i]); } return sum / actual.length; } /** * 计算决定系数(R²) */ public static double r2(double[] actual, double[] predicted) { if (actual.length != predicted.length) { throw new IllegalArgumentException("Arrays must have same length"); } double mean = Arrays.stream(actual).average().orElse(0.0); double ssRes = 0.0; double ssTot = 0.0; for (int i = 0; i < actual.length; i++) { ssRes += (actual[i] - predicted[i]) * (actual[i] - predicted[i]); ssTot += (actual[i] - mean) * (actual[i] > mean ? 1 : -1);
}

return 1 – (ssRes / ssTot);
}

/**
* 计算准确率(分类)
*/
public static double accuracy(int[] actual, int[] predicted) {
if (actual.length != predicted.length) {
throw new IllegalArgumentException(“Arrays must have same length”);
}

int correct = 0;
for (int i = 0; i < actual.length; i++) { if (actual[i] == predicted[i]) { correct++; } } return (double) correct / actual.length; } /** * 计算精确率、召回率和F1分数 */ public static Map precisionRecallF1(int[] actual, int[] predicted, int positiveClass) {
int tp = 0, fp = 0, fn = 0;

for (int i = 0; i < actual.length; i++) { if (predicted[i] == positiveClass && actual[i] == positiveClass) { tp++; } else if (predicted[i] == positiveClass && actual[i] != positiveClass) { fp++; } else if (predicted[i] != positiveClass && actual[i] == positiveClass) { fn++; } } double precision = (tp + fp == 0) ? 0 : (double) tp / (tp + fp); double recall = (tp + fn == 0) ? 0 : (double) tp / (tp + fn); double f1 = (precision + recall == 0) ? 0 : 2 * precision * recall / (precision + recall); Map metrics = new HashMap<>();
metrics.put(“precision”, precision);
metrics.put(“recall”, recall);
metrics.put(“f1”, f1);

return metrics;
}

/**
* 计算推荐系统的Precision@K
* @param recommended 推荐列表
* @param relevant 相关物品列表
* @param k topK
* @return Precision@K
*/
public static double precisionAtK(List recommended,
Set relevant, int k) {
if (k == 0) return 0.0;

int count = 0;
for (int i = 0; i < Math.min(k, recommended.size()); i++) { if (relevant.contains(recommended.get(i))) { count++; } } return (double) count / k; } /** * 计算推荐系统的Recall@K */ public static double recallAtK(List recommended,
Set relevant, int k) {
if (relevant.isEmpty()) return 0.0;

int count = 0;
for (int i = 0; i < Math.min(k, recommended.size()); i++) { if (relevant.contains(recommended.get(i))) { count++; } } return (double) count / relevant.size(); } /** * 计算推荐系统的NDCG@K */ public static double ndcgAtK(List recommended,
Map relevanceScores, int k) {
// 计算IDCG(理想DCG)
List idealRelevances = new ArrayList<>(relevanceScores.values());
Collections.sort(idealRelevances, Collections.reverseOrder());

double idcg = 0.0;
for (int i = 0; i < Math.min(k, idealRelevances.size()); i++) { idcg += idealRelevances.get(i) / Math.log(i + 2); } if (idcg == 0) return 0.0; // 计算DCG double dcg = 0.0; for (int i = 0; i < Math.min(k, recommended.size()); i++) { int itemId = recommended.get(i); double relevance = relevanceScores.getOrDefault(itemId, 0.0); dcg += relevance / Math.log(i + 2); } return dcg / idcg; } } 5.2 评估器 // src/main/java/com/example/ml/evaluation/Evaluator.java package com.example.ml.evaluation; import com.example.ml.models.recommendation.CollaborativeFiltering; import com.example.ml.models.recommendation.ContentBasedRecommender; import com.example.ml.models.recommendation.HybridRecommender; import com.example.ml.models.prediction.LinearRegression; import com.example.ml.models.prediction.DecisionTree; import com.example.ml.models.prediction.RandomForest; import java.util.*; /** * 模型评估器 */ public class Evaluator { /** * 评估推荐系统 */ public static void evaluateRecommender(CollaborativeFiltering recommender, List testSet,
int topK) {
Map> userRelevantItems = new HashMap<>();
Map> userRecommendedItems = new HashMap<>();

// 收集每个用户的相关物品(测试集中的评分)
for (double[] rating : testSet) {
int userId = (int) rating[0];
int itemId = (int) rating[1];
userRelevantItems.computeIfAbsent(userId, k -> new HashSet<>()).add(itemId);
}

// 为每个用户生成推荐并收集
for (int userId : userRelevantItems.keySet()) {
List> recommendations =
recommender.recommendByUser(userId, topK);

Set recSet = new HashSet<>();
for (Map.Entry entry : recommendations) {
recSet.add(entry.getKey());
}
userRecommendedItems.put(userId, recSet);
}

// 计算平均指标
double avgPrecision = 0.0;
double avgRecall = 0.0;
int userCount = 0;

for (int userId : userRelevantItems.keySet()) {
Set relevant = userRelevantItems.get(userId);
Set recommended = userRecommendedItems.get(userId);

if (!relevant.isEmpty() && !recommended.isEmpty()) {
double precision = Metrics.precisionAtK(
new ArrayList<>(recommended), relevant, topK);
double recall = Metrics.recallAtK(
new ArrayList<>(recommended), relevant, topK);

avgPrecision += precision;
avgRecall += recall;
userCount++;
}
}

if (userCount > 0) {
avgPrecision /= userCount;
avgRecall /= userCount;
}

System.out.println(“=== 推荐系统评估结果 ===”);
System.out.println(“Precision@” + topK + “: ” + String.format(“%.4f”, avgPrecision));
System.out.println(“Recall@” + topK + “: ” + String.format(“%.4f”, avgRecall));
System.out.println(“F1 Score@” + topK + “: ” +
String.format(“%.4f”, 2 * avgPrecision * avgRecall /
(avgPrecision + avgRecall + 1e-10)));
}

/**
* 评估预测模型
*/
public static void evaluatePredictor(LinearRegression model,
double[][] XTest, double[] yTest) {
double[] predictions = model.predict(XTest);

double mse = Metrics.mse(yTest, predictions);
double rmse = Metrics.rmse(yTest, predictions);
double mae = Metrics.mae(yTest, predictions);
double r2 = Metrics.r2(yTest, predictions);

System.out.println(“=== 线性回归评估结果 ===”);
System.out.println(“MSE: ” + String.format(“%.4f”, mse));
System.out.println(“RMSE: ” + String.format(“%.4f”, rmse));
System.out.println(“MAE: ” + String.format(“%.4f”, mae));
System.out.println(“R²: ” + String.format(“%.4f”, r2));
}

/**
* 评估决策树/随机森林
*/
public static void evaluateClassifier(DecisionTree model,
double[][] XTest, double[] yTest) {
int[] predictions = new int[XTest.length];
int[] actual = new int[yTest.length];

for (int i = 0; i < XTest.length; i++) { predictions[i] = (int) Math.round(model.predict(XTest[i])); actual[i] = (int) Math.round(yTest[i]); } double accuracy = Metrics.accuracy(actual, predictions); Map prf1 = Metrics.precisionRecallF1(actual, predictions, 1);

System.out.println(“=== 决策树评估结果 ===”);
System.out.println(“Accuracy: ” + String.format(“%.4f”, accuracy));
System.out.println(“Precision: ” + String.format(“%.4f”, prf1.get(“precision”)));
System.out.println(“Recall: ” + String.format(“%.4f”, prf1.get(“recall”)));
System.out.println(“F1 Score: ” + String.format(“%.4f”, prf1.get(“f1”)));
}

/**
* 评估随机森林
*/
public static void evaluateClassifier(RandomForest model,
double[][] XTest, double[] yTest) {
int[] predictions = new int[XTest.length];
int[] actual = new int[yTest.length];

for (int i = 0; i < XTest.length; i++) { predictions[i] = model.predictClass(XTest[i]); actual[i] = (int) Math.round(yTest[i]); } double accuracy = Metrics.accuracy(actual, predictions); Map prf1 = Metrics.precisionRecallF1(actual, predictions, 1);

System.out.println(“=== 随机森林评估结果 ===”);
System.out.println(“Accuracy: ” + String.format(“%.4f”, accuracy));
System.out.println(“Precision: ” + String.format(“%.4f”, prf1.get(“precision”)));
System.out.println(“Recall: ” + String.format(“%.4f”, prf1.get(“recall”)));
System.out.println(“F1 Score: ” + String.format(“%.4f”, prf1.get(“f1”)));
}
}
六、完整示例与使用指南
6.1 推荐系统完整示例
// src/main/java/com/example/ml/RecommendationSystemDemo.java
package com.example.ml;

import com.example.ml.data.DataLoader;
import com.example.ml.evaluation.Evaluator;
import com.example.ml.models.recommendation.CollaborativeFiltering;
import com.example.ml.models.recommendation.ContentBasedRecommender;
import com.example.ml.models.recommendation.HybridRecommender;
import com.example.ml.data.FeatureExtractor;

import java.io.IOException;
import java.util.*;

/**
* 推荐系统完整示例
*/
public class RecommendationSystemDemo {

public static void main(String[] args) throws IOException {
System.out.println(“=== Java机器学习推荐系统实战 ===”);

// 1. 加载数据
// 假设CSV格式:userID,itemID,rating
// 示例数据:1,1,5.0; 1,2,4.0; 2,1,3.0; 2,3,5.0; 3,2,4.0; 3,3,2.0
List ratings = DataLoader.loadRatingsFromCSV(“data/ratings.csv”);

// 2. 数据预处理
int userCount = 4; // 假设4个用户
int itemCount = 4; // 假设4个物品
double[][] userItemMatrix = DataLoader.convertToUserItemMatrix(ratings, userCount, itemCount);
double[][] normalizedMatrix = DataLoader.normalizeMatrix(userItemMatrix);

// 3. 数据集划分
Map> dataset = DataLoader.splitDataset(ratings, 0.8);
List trainSet = dataset.get(“train”);
List testSet = dataset.get(“test”);

// 4. 构建训练矩阵
double[][] trainMatrix = DataLoader.convertToUserItemMatrix(trainSet, userCount, itemCount);

// 5. 创建协同过滤推荐器
CollaborativeFiltering cf = new CollaborativeFiltering(trainMatrix, “cosine”);

// 6. 为用户1生成推荐
int targetUser = 1;
int topK = 2;

System.out.println(“\n— 协同过滤推荐结果 —“);
List> cfRecommendations =
cf.recommendByUser(targetUser, topK);

System.out.println(“用户 ” + targetUser + ” 的推荐列表:”);
for (Map.Entry entry : cfRecommendations) {
System.out.println(” 物品 ” + entry.getKey() + ” – 预测评分: ” +
String.format(“%.2f”, entry.getValue()));
}

// 7. 基于内容的推荐(需要物品特征)
// 构建物品特征(示例:假设物品有类别特征)
Map> itemMetadata = new HashMap<>();
Map item1Meta = new HashMap<>();
item1Meta.put(“category_A”, true);
item1Meta.put(“category_B”, false);
itemMetadata.put(1, item1Meta);

Map item2Meta = new HashMap<>();
item2Meta.put(“category_A”, false);
item2Meta.put(“category_B”, true);
itemMetadata.put(2, item2Meta);

Map item3Meta = new HashMap<>();
item3Meta.put(“category_A”, true);
item3Meta.put(“category_B”, true);
itemMetadata.put(3, item3Meta);

Map itemFeatures =
FeatureExtractor.extractItemFeatures(itemMetadata);

// 构建用户评分历史
Map> userRatingsMap = new HashMap<>();
for (double[] rating : trainSet) {
int userId = (int) rating[0];
userRatingsMap.computeIfAbsent(userId, k -> new ArrayList<>()).add(rating);
}

ContentBasedRecommender cb =
new ContentBasedRecommender(itemFeatures, userRatingsMap);

System.out.println(“\n— 基于内容的推荐结果 —“);
List> cbRecommendations =
cb.recommend(targetUser, topK);

System.out.println(“用户 ” + targetUser + ” 的推荐列表:”);
for (Map.Entry entry : cbRecommendations) {
System.out.println(” 物品 ” + entry.getKey() + ” – 相似度: ” +
String.format(“%.2f”, entry.getValue()));
}

// 8. 混合推荐
HybridRecommender hybrid = new HybridRecommender(cf, cb, 0.6, 0.4);

System.out.println(“\n— 混合推荐结果 —“);
List> hybridRecommendations =
hybrid.recommend(targetUser, topK);

System.out.println(“用户 ” + targetUser + ” 的推荐列表:”);
for (Map.Entry entry : hybridRecommendations) {
System.out.println(” 物品 ” + entry.getKey() + ” – 综合评分: ” +
String.format(“%.2f”, entry.getValue()));
}

// 9. 评估推荐系统
System.out.println(“\n— 推荐系统评估 —“);
Evaluator.evaluateRecommender(cf, testSet, topK);
}
}
6.2 预测模型完整示例
// src/main/java/com/example/ml/PredictionModelDemo.java
package com.example.ml;

import com.example.ml.evaluation.Evaluator;
import com.example.ml.models.prediction.LinearRegression;
import com.example.ml.models.prediction.DecisionTree;
import com.example.ml.models.prediction.RandomForest;

import java.util.Arrays;
import java.util.Random;

/**
* 预测模型完整示例
*/
public class PredictionModelDemo {

public static void main(String[] args) {
System.out.println(“=== Java机器学习预测模型实战 ===”);

// 1. 生成示例数据
// 特征:房屋面积、卧室数量、房龄
// 目标:房价
double[][] X = {
{1500, 3, 10},
{2000, 4, 5},
{1200, 2, 15},
{1800, 3, 8},
{2200, 5, 3},
{1600, 3, 12},
{1900, 4, 6},
{1300, 2, 20},
{2100, 4, 4},
{1700, 3, 9}
};

double[] y = {300, 450, 250, 380, 520, 320, 420, 280, 480, 350};

// 2. 数据集划分
int n = X.length;
int trainSize = (int) (n * 0.8);
double[][] XTrain = new double[trainSize][];
double[] yTrain = new double[trainSize];
double[][] XTest = new double[n – trainSize][];
double[] yTest = new double[n – trainSize];

System.arraycopy(X, 0, XTrain, 0, trainSize);
System.arraycopy(y, 0, yTrain, 0, trainSize);
System.arraycopy(X, trainSize, XTest, 0, n – trainSize);
System.arraycopy(y, trainSize, yTest, 0, n – trainSize);

// 3. 线性回归
System.out.println(“\n— 线性回归训练 —“);
LinearRegression lr = new LinearRegression(0.01, 1000, 1e-6);
lr.train(XTrain, yTrain);

// 4. 评估线性回归
Evaluator.evaluatePredictor(lr, XTest, yTest);

// 5. 决策树
System.out.println(“\n— 决策树训练 —“);
// 将回归问题转换为分类问题(简化示例)
double[] yTrainClass = Arrays.stream(yTrain)
.map(v -> v > 350 ? 1.0 : 0.0)
.toArray();
double[] yTestClass = Arrays.stream(yTest)
.map(v -> v > 350 ? 1.0 : 0.0)
.toArray();

DecisionTree dt = new DecisionTree(3, 2);
dt.train(XTrain, yTrainClass);

// 6. 评估决策树
Evaluator.evaluateClassifier(dt, XTest, yTestClass);

// 7. 随机森林
System.out.println(“\n— 随机森林训练 —“);
RandomForest rf = new RandomForest(10, 3, 2, 2);
rf.train(XTrain, yTrainClass);

// 8. 评估随机森林
Evaluator.evaluateClassifier(rf, XTest, yTestClass);

// 9. 预测新样本
System.out.println(“\n— 新样本预测 —“);
double[] newSample = {1750, 3, 7};

double lrPrediction = lr.predict(newSample);
int dtPrediction = (int) Math.round(dt.predict(newSample));
int rfPrediction = rf.predictClass(newSample);

System.out.println(“新样本: ” + Arrays.toString(newSample));
System.out.println(“线性回归预测房价: ” + String.format(“%.2f”, lrPrediction));
System.out.println(“决策树预测类别: ” + dtPrediction + ” (1=高价, 0=低价)”);
System.out.println(“随机森林预测类别: ” + rfPrediction + ” (1=高价, 0=低价)”);
}
}
6.3 数据格式说明
为了运行上述示例,需要准备CSV格式的数据文件:
ratings.csv
(推荐系统数据):
userID,itemID,rating
1,1,5.0
1,2,4.0
1,3,3.0
2,1,3.0
2,2,4.0
2,3,5.0
3,1,4.0
3,2,2.0
3,3,5.0
4,1,2.0
4,2,5.0
4,3,4.0
features.csv
(预测模型数据):
feature1,feature2,feature3,target
1500,3,10,300
2000,4,5,450
1200,2,15,250
1800,3,8,380
2200,5,3,520
1600,3,12,320
1900,4,6,420
1300,2,20,280
2100,4,4,480
1700,3,9,350
七、性能优化与最佳实践
7.1 性能优化技巧
// src/main/java/com/example/ml/utils/OptimizationUtils.java
package com.example.ml.utils;

import java.util.*;
import java.util.concurrent.*;

/**
* 性能优化工具类
*/
public class OptimizationUtils {

/**
* 并行计算相似度矩阵
* 使用多线程加速大规模计算
*/
public static double[][] parallelComputeSimilarity(double[][] matrix,
String method) {
int n = matrix.length;
double[][] similarity = new double[n][n];

// 使用ForkJoinPool进行并行计算
ForkJoinPool pool = ForkJoinPool.commonPool();

List> tasks = new ArrayList<>();
for (int i = 0; i < n; i++) { final int row = i; tasks.add(() -> {
for (int j = row + 1; j < n; j++) { double sim = calculateSimilarity(matrix[row], matrix[j], method); similarity[row][j] = sim; similarity[j][row] = sim; // 对称矩阵 } similarity[row][row] = 1.0; return null; }); } try { pool.invokeAll(tasks); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } // 设置对角线 for (int i = 0; i < n; i++) { similarity[i][i] = 1.0; } return similarity; } private static double calculateSimilarity(double[] vec1, double[] vec2, String method) { if ("cosine".equals(method)) { return cosineSimilarity(vec1, vec2); } else { return pearsonCorrelation(vec1, vec2); } } private static double cosineSimilarity(double[] vec1, double[] vec2) { double dotProduct = 0.0; double norm1 = 0.0; double norm2 = 0.0; for (int i = 0; i < vec1.length; i++) { if (vec1[i] > 0 && vec2[i] > 0) {
dotProduct += vec1[i] * vec2[i];
norm1 += vec1[i] * vec1[i];
norm2 += vec2[i] * vec2[i];
}
}

if (norm1 == 0 || norm2 == 0) return 0.0;
return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
}

private static double pearsonCorrelation(double[] vec1, double[] vec2) {
double sum1 = 0.0, sum2 = 0.0;
double sum1Sq = 0.0, sum2Sq = 0.0;
double pSum = 0.0;
int n = 0;

for (int i = 0; i < vec1.length; i++) { if (vec1[i] > 0 && vec2[i] > 0) {
sum1 += vec1[i];
sum2 += vec2[i];
sum1Sq += vec1[i] * vec1[i];
sum2Sq += vec2[i] * vec2[i];
pSum += vec1[i] * vec2[i];
n++;
}
}

if (n == 0) return 0.0;

double num = pSum – (sum1 * sum2 / n);
double den = Math.sqrt((sum1Sq – sum1 * sum1 / n) * (sum2Sq – sum2 * sum2 / n));

return den == 0.0 ? 0.0 : num / den;
}

/**
* 矩阵分解(SVD近似)
* 用于降维和推荐系统
*/
public static class MatrixFactorization {
private double[][] userFactors;
private double[][] itemFactors;
private int k; // 隐因子维度

public MatrixFactorization(int k) {
this.k = k;
}

/**
* 训练矩阵分解模型
* 使用随机梯度下降
*/
public void train(double[][] R, int maxIter, double learningRate, double lambda) {
int nUsers = R.length;
int nItems = R[0].length;

// 初始化因子矩阵
Random rand = new Random();
userFactors = new double[nUsers][k];
itemFactors = new double[nItems][k];

for (int i = 0; i < nUsers; i++) { for (int j = 0; j < k; j++) { userFactors[i][j] = rand.nextDouble() * 0.1; } } for (int i = 0; i < nItems; i++) { for (int j = 0; j < k; j++) { itemFactors[i][j] = rand.nextDouble() * 0.1; } } // SGD训练 for (int iter = 0; iter < maxIter; iter++) { double errorSum = 0.0; int count = 0; for (int u = 0; u < nUsers; u++) { for (int i = 0; i < nItems; i++) { if (R[u][i] > 0) {
// 预测评分
double prediction = predict(u, i);
double error = R[u][i] – prediction;
errorSum += error * error;
count++;

// 更新因子
for (int f = 0; f < k; f++) { double userFactor = userFactors[u][f]; double itemFactor = itemFactors[u][f]; userFactors[u][f] += learningRate * (2 * error * itemFactor - 2 * lambda * userFactor); itemFactors[i][f] += learningRate * (2 * error * userFactor - 2 * lambda * itemFactor); } } } } if (iter % 10 == 0) { System.out.println("Iteration " + iter + ", RMSE: " + Math.sqrt(errorSum / count)); } } } /** * 预测评分 */ public double predict(int user, int item) { double sum = 0.0; for (int f = 0; f < k; f++) { sum += userFactors[user][f] * itemFactors[item][f]; } return sum; } /** * 获取推荐 */ public List> recommend(int userId, int topK) {
Map scores = new HashMap<>();

for (int itemId = 0; itemId < itemFactors.length; itemId++) { double score = predict(userId, itemId); scores.put(itemId, score); } return scores.entrySet().stream() .sorted((e1, e2) -> Double.compare(e2.getValue(), e1.getValue()))
.limit(topK)
.collect(Collectors.toList());
}
}
}
7.2 缓存策略
// src/main/java/com/example/ml/utils/CacheManager.java
package com.example.ml.utils;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* 缓存管理器
* 用于缓存计算结果,避免重复计算
*/
public class CacheManager {

private static final Map cache = new ConcurrentHashMap<>();

/**
* 缓存数据
*/
public static void put(String key, Object value) {
cache.put(key, value);
}

/**
* 获取缓存数据
*/
public static T get(String key, Class clazz) {
Object value = cache.get(key);
if (value != null && clazz.isInstance(value)) {
return clazz.cast(value);
}
return null;
}

/**
* 检查缓存是否存在
*/
public static boolean contains(String key) {
return cache.containsKey(key);
}

/**
* 清除缓存
*/
public static void clear() {
cache.clear();
}

/**
* 带缓存的相似度计算
*/
public static double[][] getCachedSimilarityMatrix(
double[][] matrix, String method, String cacheKey) {

if (contains(cacheKey)) {
System.out.println(“使用缓存的相似度矩阵: ” + cacheKey);
return get(cacheKey, double[][].class);
}

System.out.println(“计算相似度矩阵: ” + cacheKey);
double[][] similarity = OptimizationUtils.parallelComputeSimilarity(matrix, method);
put(cacheKey, similarity);

return similarity;
}
}
八、总结与展望
8.1 关键要点总结
Java机器学习优势
:
企业级应用集成方便
高性能计算能力
丰富的生态系统
类型安全和编译时检查
核心组件
:
数据处理:加载、预处理、特征工程
推荐系统:协同过滤、基于内容、混合推荐
预测模型:线性回归、决策树、随机森林
模型评估:多种指标和评估方法
性能优化
:
并行计算
矩阵分解
缓存策略
内存管理
8.2 生产环境部署建议
模型持久化
:
将训练好的模型保存到文件
支持模型版本管理
实现热加载机制
API设计
:
RESTful接口暴露推荐服务
批量预测支持
实时更新机制
监控与调优
:
记录预测性能指标
A/B测试框架
自动化参数调优
8.3 未来扩展方向
深度学习集成
:
考虑使用Deeplearning4j等Java深度学习框架
实现神经网络推荐系统
流式处理
:
集成Apache Kafka进行实时推荐
在线学习算法
分布式计算
:
使用Apache Spark MLlib
分布式矩阵运算
通过本文的详细讲解和完整代码示例,您应该已经掌握了使用Java构建机器学习推荐系统和预测模型的核心技术。这些代码可以直接用于实际项目,也可以作为学习和研究的基础。记住在实际应用中要根据具体场景调整参数、优化性能,并持续监控模型效果。