城市脉搏的解码者:Java如何用AI“听诊”交通数据,精准识别高峰与低峰?——从原始流量到智能决策的全链路深度实战**
首先,定义交通数据的基本单元。/*** 表示某一时刻某一路段的交通状态快照* 这是所有分析的基础数据单元*//*** 时间戳,精确到分钟* 是最重要的特征之一*//*** 路段ID,标识具体道路* 可扩展为GPS坐标范围*//*** 平均车速 (km/h)* 核心指标:速度越低,拥堵越严重*//*** 车流量 (辆/小时)* 高流量不一定拥堵,需结合速度判断*//*** 占有率 (%)* 雷达或摄
·
一、交通分类的本质:从“看表”到“读心”
传统方法认为“17:00-19:00 就是高峰”,但现实远比这复杂:
- 工作日 vs 周末:周五晚高峰可能提前。
- 天气影响:暴雨天通勤时间延长30%。
- 突发事件:一场演唱会结束瞬间引发区域拥堵。
- 长期趋势:新地铁线开通后,旧主干道车流下降。
因此,真正的交通分类必须是多维、动态、可学习的。
核心挑战:
- 数据从哪来? —— 模拟真实交通传感器数据流。
- 特征怎么选? —— 如何从原始数据中提取有效信息?
- 模型如何训练? —— 选择最适合时序分类的算法。
- 如何实时预测? —— 在毫秒级响应用户请求。
我们将用 Java 逐一攻克。
二、数据模型:定义交通状态
首先,定义交通数据的基本单元。
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
/**
* 表示某一时刻某一路段的交通状态快照
* 这是所有分析的基础数据单元
*/
public class TrafficSnapshot {
/**
* 时间戳,精确到分钟
* 是最重要的特征之一
*/
private LocalDateTime timestamp;
/**
* 路段ID,标识具体道路
* 可扩展为GPS坐标范围
*/
private String roadSegmentId;
/**
* 平均车速 (km/h)
* 核心指标:速度越低,拥堵越严重
*/
private double averageSpeed;
/**
* 车流量 (辆/小时)
* 高流量不一定拥堵,需结合速度判断
*/
private int vehicleCount;
/**
* 占有率 (%)
* 雷达或摄像头检测到车辆占用道路的时间比例
* >70% 通常表示拥堵
*/
private double occupancyRate;
/**
* 天气状况
* 晴、雨、雪、雾等
* 显著影响交通
*/
private WeatherCondition weather;
/**
* 是否为节假日
* 国家法定假日或地方特殊节日
*/
private boolean isHoliday;
/**
* 事件标志
* 如事故、施工、大型活动等
*/
private TrafficEvent eventFlag;
// 构造函数
public TrafficSnapshot(LocalDateTime timestamp,
String roadSegmentId,
double averageSpeed,
int vehicleCount,
double occupancyRate,
WeatherCondition weather,
boolean isHoliday,
TrafficEvent eventFlag) {
this.timestamp = timestamp;
this.roadSegmentId = roadSegmentId;
this.averageSpeed = averageSpeed;
this.vehicleCount = vehicleCount;
this.occupancyRate = occupancyRate;
this.weather = weather;
this.isHoliday = isHoliday;
this.eventFlag = eventFlag;
}
// Getters and Setters ...
public LocalDateTime getTimestamp() { return timestamp; }
public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; }
public String getRoadSegmentId() { return roadSegmentId; }
public void setRoadSegmentId(String roadSegmentId) { this.roadSegmentId = roadSegmentId; }
public double getAverageSpeed() { return averageSpeed; }
public void setAverageSpeed(double averageSpeed) { this.averageSpeed = averageSpeed; }
public int getVehicleCount() { return vehicleCount; }
public void setVehicleCount(int vehicleCount) { this.vehicleCount = vehicleCount; }
public double getOccupancyRate() { return occupancyRate; }
public void setOccupancyRate(double occupancyRate) { this.occupancyRate = occupancyRate; }
public WeatherCondition getWeather() { return weather; }
public void setWeather(WeatherCondition weather) { this.weather = weather; }
public boolean isHoliday() { return isHoliday; }
public void setHoliday(boolean holiday) { isHoliday = holiday; }
public TrafficEvent getEventFlag() { return eventFlag; }
public void setEventFlag(TrafficEvent eventFlag) { this.eventFlag = eventFlag; }
/**
* 判断当前是否为高峰时段(人工标注,用于训练)
* 实际系统中可通过历史平均速度对比生成
*/
public boolean isPeakHour() {
// 简化规则:速度 < 20 km/h 且 占有率 > 65% 视为高峰
return averageSpeed < 20.0 && occupancyRate > 0.65;
}
@Override
public String toString() {
return String.format("TrafficSnapshot{time=%s, road=%s, speed=%.1f, count=%d, occ=%.1f%%, weather=%s, holiday=%b, event=%s}",
timestamp, roadSegmentId, averageSpeed, vehicleCount, occupancyRate*100, weather, isHoliday, eventFlag);
}
}
/**
* 天气枚举
*/
enum WeatherCondition {
CLEAR, RAIN, SNOW, FOG, CLOUDY
}
/**
* 交通事件枚举
*/
enum TrafficEvent {
NONE, ACCIDENT, CONSTRUCTION, EVENT, ROAD_CLOSED
}
三、特征工程:从原始数据到“诊断依据”
原始数据不能直接喂给模型,必须进行特征提取。
import java.util.*;
import java.util.stream.Collectors;
/**
* 特征工程处理器
* 将原始 TrafficSnapshot 转换为机器学习可用的数值特征向量
*/
public class FeatureExtractor {
/**
* 提取单个样本的特征向量
* 返回一个双精度数组,供模型使用
*/
public double[] extractFeatures(TrafficSnapshot snapshot, List<TrafficSnapshot> historicalData) {
List<Double> features = new ArrayList<>();
// 1. 时间特征 (Time-based Features)
features.addAll(extractTimeFeatures(snapshot.getTimestamp()));
// 2. 当前状态特征 (Current State)
features.add(snapshot.getAverageSpeed());
features.add(snapshot.getVehicleCount());
features.add(snapshot.getOccupancyRate());
// 3. 天气独热编码 (One-Hot Encoding for Weather)
features.addAll(encodeWeather(snapshot.getWeather()));
// 4. 事件标志 (Event Flags)
features.add(snapshot.isHoliday() ? 1.0 : 0.0);
features.add(eventToNumeric(snapshot.getEventFlag()));
// 5. 历史对比特征 (Historical Comparison)
if (historicalData != null && !historicalData.isEmpty()) {
features.addAll(extractHistoricalFeatures(snapshot, historicalData));
}
// 6. 流动性指标 (Derived Metrics)
features.add(calculateCongestionIndex(snapshot));
features.add(calculateFlowEfficiency(snapshot));
// 转换为数组
return features.stream().mapToDouble(Double::doubleValue).toArray();
}
/**
* 提取时间相关特征
* 将时间转换为周期性数值,便于模型理解
*/
private List<Double> extractTimeFeatures(LocalDateTime time) {
List<Double> timeFeatures = new ArrayList<>();
// 小时 (0-23) -> 正弦/余弦编码,解决 23 和 0 的跳跃问题
int hour = time.getHour();
timeFeatures.add(Math.sin(2 * Math.PI * hour / 24.0));
timeFeatures.add(Math.cos(2 * Math.PI * hour / 24.0));
// 星期几 (1=周一, 7=周日)
int dayOfWeek = time.getDayOfWeek().getValue();
timeFeatures.add((double) dayOfWeek);
// 是否工作日
timeFeatures.add(dayOfWeek <= 5 ? 1.0 : 0.0);
// 月份 (季节性)
int month = time.getMonthValue();
timeFeatures.add((double) month);
// 季节 (1=冬, 2=春, 3=夏, 4=秋)
int season = (month % 12) / 3 + 1;
timeFeatures.add((double) season);
return timeFeatures;
}
/**
* 天气独热编码
* [晴, 雨, 雪, 雾, 多云]
*/
private List<Double> encodeWeather(WeatherCondition weather) {
List<Double> encoding = Arrays.asList(0.0, 0.0, 0.0, 0.0, 0.0);
switch (weather) {
case CLEAR: encoding.set(0, 1.0); break;
case RAIN: encoding.set(1, 1.0); break;
case SNOW: encoding.set(2, 1.0); break;
case FOG: encoding.set(3, 1.0); break;
case CLOUDY:encoding.set(4, 1.0); break;
}
return encoding;
}
/**
* 事件转数值
*/
private double eventToNumeric(TrafficEvent event) {
switch (event) {
case NONE: return 0.0;
case ACCIDENT: return 3.0;
case CONSTRUCTION:return 2.0;
case EVENT: return 2.5;
case ROAD_CLOSED:return 4.0;
default: return 0.0;
}
}
/**
* 提取历史对比特征
* 让模型理解“当前情况相比平时如何”
*/
private List<Double> extractHistoricalFeatures(TrafficSnapshot current, List<TrafficSnapshot> history) {
List<Double> histFeatures = new ArrayList<>();
// 过滤出同一路段、同一星期几的历史数据
List<TrafficSnapshot> relevantHistory = history.stream()
.filter(s -> s.getRoadSegmentId().equals(current.getRoadSegmentId()))
.filter(s -> s.getTimestamp().getDayOfWeek() == current.getTimestamp().getDayOfWeek())
.collect(Collectors.toList());
if (relevantHistory.isEmpty()) {
// 无历史数据,返回默认值
histFeatures.add(0.0); // 速度偏差
histFeatures.add(0.0); // 流量偏差
histFeatures.add(0.0); // 拥堵指数偏差
return histFeatures;
}
// 计算历史平均值
double avgSpeedHist = relevantHistory.stream().mapToDouble(TrafficSnapshot::getAverageSpeed).average().orElse(30.0);
double avgCountHist = relevantHistory.stream().mapToDouble(s -> s.getVehicleCount()).average().orElse(100.0);
double avgOccHist = relevantHistory.stream().mapToDouble(s -> s.getOccupancyRate()).average().orElse(0.5);
// 计算当前与历史的偏差(标准化)
double speedDeviation = (current.getAverageSpeed() - avgSpeedHist) / (avgSpeedHist + 1.0);
double countDeviation = (current.getVehicleCount() - avgCountHist) / (avgCountHist + 1.0);
double occDeviation = (current.getOccupancyRate() - avgOccHist) / (avgOccHist + 0.01);
histFeatures.add(speedDeviation);
histFeatures.add(countDeviation);
histFeatures.add(occDeviation);
return histFeatures;
}
/**
* 拥堵指数 (0-1)
* 综合速度和占有率
*/
private double calculateCongestionIndex(TrafficSnapshot snapshot) {
// 速度权重 0.6, 占有率权重 0.4
double speedScore = 1.0 - Math.min(snapshot.getAverageSpeed() / 60.0, 1.0); // 60km/h 为畅通
double occScore = snapshot.getOccupancyRate();
return 0.6 * speedScore + 0.4 * occScore;
}
/**
* 流动效率 (0-1)
* 高效 = 高流量 + 高速度
*/
private double calculateFlowEfficiency(TrafficSnapshot snapshot) {
double normalizedSpeed = snapshot.getAverageSpeed() / 80.0; // 假设限速80
double normalizedFlow = snapshot.getVehicleCount() / 500.0; // 假设最大流量500
return Math.min(normalizedSpeed * normalizedFlow, 1.0);
}
}
四、机器学习模型:三大算法深度实现
1. 逻辑回归(Logistic Regression)—— 基准模型
/**
* 逻辑回归分类器
* 线性模型,解释性强,适合作为基准
*/
public class LogisticRegressionClassifier {
private double[] weights; // 模型权重
private double bias; // 偏置项
private double learningRate = 0.01;
private int maxIterations = 1000;
public LogisticRegressionClassifier() {}
/**
* Sigmoid 激活函数
* 将线性输出映射到 (0,1) 概率
*/
private double sigmoid(double z) {
// 防止溢出
if (z > 10.0) return 1.0;
if (z < -10.0) return 0.0;
return 1.0 / (1.0 + Math.exp(-z));
}
/**
* 训练模型
*/
public void train(List<double[]> features, List<Boolean> labels) {
if (features.isEmpty() || features.size() != labels.size()) {
throw new IllegalArgumentException("Features and labels size mismatch");
}
int nSamples = features.size();
int nFeatures = features.get(0).length;
// 初始化权重
weights = new double[nFeatures];
bias = 0.0;
// 随机初始化权重(小范围)
Random rand = new Random(42);
for (int i = 0; i < nFeatures; i++) {
weights[i] = (rand.nextDouble() - 0.5) * 0.1;
}
// 梯度下降
for (int iter = 0; iter < maxIterations; iter++) {
double dwSum = 0.0;
double dbSum = 0.0;
for (int i = 0; i < nSamples; i++) {
double[] x = features.get(i);
boolean y = labels.get(i);
// 前向传播
double z = bias;
for (int j = 0; j < nFeatures; j++) {
z += weights[j] * x[j];
}
double a = sigmoid(z);
// 计算损失梯度
double dz = a - (y ? 1.0 : 0.0);
dbSum += dz;
for (int j = 0; j < nFeatures; j++) {
weights[j] -= learningRate * dz * x[j]; // 在这里更新更高效
}
}
// 更新偏置
bias -= learningRate * dbSum / nSamples;
// 可选:打印损失
if (iter % 100 == 0) {
double loss = computeLoss(features, labels);
System.out.printf("[LR] Iteration %d, Loss: %.6f%n", iter, loss);
}
}
}
/**
* 计算对数损失
*/
private double computeLoss(List<double[]> features, List<Boolean> labels) {
double loss = 0.0;
for (int i = 0; i < features.size(); i++) {
double[] x = features.get(i);
boolean y = labels.get(i);
double z = bias;
for (int j = 0; j < weights.length; j++) {
z += weights[j] * x[j];
}
double a = sigmoid(z);
// 防止 log(0)
a = Math.max(1e-15, Math.min(1 - 1e-15, a));
loss += y ? -Math.log(a) : -Math.log(1 - a);
}
return loss / features.size();
}
/**
* 预测
* @return 概率值 [0,1]
*/
public double predictProbability(double[] features) {
if (weights == null) throw new IllegalStateException("Model not trained");
double z = bias;
for (int i = 0; i < weights.length; i++) {
z += weights[i] * features[i];
}
return sigmoid(z);
}
/**
* 分类
* @param threshold 决策阈值,默认 0.5
* @return true=高峰, false=低峰
*/
public boolean predict(double[] features, double threshold) {
return predictProbability(features) >= threshold;
}
}
2. 随机森林(Random Forest)—— 非线性王者
import java.util.*;
/**
* 随机森林分类器
* 集成多个决策树,抗过拟合,处理非线性关系
*/
public class RandomForestClassifier {
private List<DecisionTree> trees;
private int nTrees = 100;
private int maxDepth = 10;
private int minSamplesSplit = 2;
private Random random;
public RandomForestClassifier() {
this.random = new Random(42);
}
/**
* 训练随机森林
*/
public void train(List<double[]> features, List<Boolean> labels) {
trees = new ArrayList<>();
int nSamples = features.size();
int nFeatures = features.get(0).length;
int nFeaturesSubset = (int) Math.sqrt(nFeatures); // 每棵树随机选择 sqrt(n) 个特征
for (int t = 0; t < nTrees; t++) {
// 有放回抽样(Bootstrap)
List<Integer> bootstrapIndices = new ArrayList<>();
for (int i = 0; i < nSamples; i++) {
bootstrapIndices.add(random.nextInt(nSamples));
}
// 构建子集
List<double[]> treeFeatures = new ArrayList<>();
List<Boolean> treeLabels = new ArrayList<>();
for (int idx : bootstrapIndices) {
treeFeatures.add(features.get(idx));
treeLabels.add(labels.get(idx));
}
// 训练单棵决策树
DecisionTree tree = new DecisionTree(maxDepth, minSamplesSplit, random.nextLong());
tree.train(treeFeatures, treeLabels, nFeaturesSubset);
trees.add(tree);
}
}
/**
* 预测(投票机制)
*/
public boolean predict(double[] features) {
int peakVotes = 0;
for (DecisionTree tree : trees) {
if (tree.predict(features)) {
peakVotes++;
}
}
// 简单多数投票
return peakVotes > trees.size() / 2;
}
/**
* 决策树内部类
*/
private static class DecisionTree {
private Node root;
private int maxDepth;
private int minSamplesSplit;
private Random random;
public DecisionTree(int maxDepth, int minSamplesSplit, long seed) {
this.maxDepth = maxDepth;
this.minSamplesSplit = minSamplesSplit;
this.random = new Random(seed);
}
public void train(List<double[]> features, List<Boolean> labels, int nFeaturesSubset) {
root = buildTree(features, labels, 0, nFeaturesSubset);
}
private Node buildTree(List<double[]> X, List<Boolean> y, int depth, int nFeaturesSubset) {
// 终止条件
if (depth >= maxDepth || y.size() <= minSamplesSplit || allSame(y)) {
boolean majorityClass = y.stream().filter(b -> b).count() > y.size() / 2;
return new Node(majorityClass);
}
// 选择最佳分割
Split bestSplit = findBestSplit(X, y, nFeaturesSubset);
if (bestSplit == null) {
boolean majorityClass = y.stream().filter(b -> b).count() > y.size() / 2;
return new Node(majorityClass);
}
// 分割数据
List<double[]> XLeft = new ArrayList<>();
List<Boolean> yLeft = new ArrayList<>();
List<double[]> XRight = new ArrayList<>();
List<Boolean> yRight = new ArrayList<>();
for (int i = 0; i < X.size(); i++) {
if (X.get(i)[bestSplit.featureIndex] <= bestSplit.threshold) {
XLeft.add(X.get(i));
yLeft.add(y.get(i));
} else {
XRight.add(X.get(i));
yRight.add(y.get(i));
}
}
Node node = new Node(bestSplit.featureIndex, bestSplit.threshold);
node.left = buildTree(XLeft, yLeft, depth + 1, nFeaturesSubset);
node.right = buildTree(XRight, yRight, depth + 1, nFeaturesSubset);
return node;
}
private Split findBestSplit(List<double[]> X, List<Boolean> y, int nFeaturesSubset) {
if (X.size() < 2) return null;
int nFeatures = X.get(0).length;
List<Integer> featureIndices = new ArrayList<>();
for (int i = 0; i < nFeatures; i++) featureIndices.add(i);
Collections.shuffle(featureIndices, random);
featureIndices = featureIndices.subList(0, Math.min(nFeaturesSubset, nFeatures));
double bestGini = Double.MAX_VALUE;
Split bestSplit = null;
for (int featIdx : featureIndices) {
// 获取该特征的所有值并排序
List<Double> values = X.stream().map(arr -> arr[featIdx]).sorted().collect(ArrayList::new, List::add, List::addAll);
// 尝试每个可能的分割点
for (int i = 0; i < values.size() - 1; i++) {
double threshold = (values.get(i) + values.get(i + 1)) / 2.0;
double gini = computeGiniImpurity(X, y, featIdx, threshold);
if (gini < bestGini) {
bestGini = gini;
bestSplit = new Split(featIdx, threshold);
}
}
}
return bestSplit;
}
private double computeGiniImpurity(List<double[]> X, List<Boolean> y, int featureIndex, double threshold) {
List<Boolean> left = new ArrayList<>();
List<Boolean> right = new ArrayList<>();
for (int i = 0; i < X.size(); i++) {
if (X.get(i)[featureIndex] <= threshold) {
left.add(y.get(i));
} else {
right.add(y.get(i));
}
}
if (left.isEmpty() || right.isEmpty()) return Double.MAX_VALUE;
double giniLeft = 1.0;
long trueLeft = left.stream().filter(b -> b).count();
double pTrue = (double) trueLeft / left.size();
giniLeft -= pTrue * pTrue + (1 - pTrue) * (1 - pTrue);
double giniRight = 1.0;
long trueRight = right.stream().filter(b -> b).count();
double pTrueR = (double) trueRight / right.size();
giniRight -= pTrueR * pTrueR + (1 - pTrueR) * (1 - pTrueR);
return (left.size() * giniLeft + right.size() * giniRight) / X.size();
}
public boolean predict(double[] features) {
return predictNode(root, features);
}
private boolean predictNode(Node node, double[] features) {
if (node.isLeaf) {
return node.prediction;
}
if (features[node.featureIndex] <= node.threshold) {
return predictNode(node.left, features);
} else {
return predictNode(node.right, features);
}
}
// 内部节点类
private static class Node {
boolean isLeaf;
boolean prediction;
int featureIndex;
double threshold;
Node left, right;
Node(boolean prediction) {
this.isLeaf = true;
this.prediction = prediction;
}
Node(int featureIndex, double threshold) {
this.isLeaf = false;
this.featureIndex = featureIndex;
this.threshold = threshold;
}
}
// 分割点
private static class Split {
int featureIndex;
double threshold;
Split(int featureIndex, double threshold) {
this.featureIndex = featureIndex;
this.threshold = threshold;
}
}
private boolean allSame(List<Boolean> list) {
if (list.isEmpty()) return true;
boolean first = list.get(0);
return list.stream().allMatch(b -> b == first);
}
}
}
3. LSTM 神经网络(长短期记忆)—— 时序之王
import java.util.*;
/**
* LSTM 神经网络分类器
* 处理时间序列依赖,适合交通模式识别
* 使用纯 Java 实现,不依赖外部框架
*/
public class LSTMLayer {
// 权重矩阵 (简化版)
private double[][] Wi, Wf, Wc, Wo; // 输入门、遗忘门、候选细胞、输出门
private double[][] Ui, Uf, Uc, Uo; // 循环权重
private double[] bi, bf, bc, bo; // 偏置
private int inputSize;
private int hiddenSize;
private Random random;
public LSTMLayer(int inputSize, int hiddenSize) {
this.inputSize = inputSize;
this.hiddenSize = hiddenSize;
this.random = new Random(42);
// 初始化权重 (Xavier 初始化)
double scale = Math.sqrt(2.0 / (inputSize + hiddenSize));
Wi = initializeMatrix(hiddenSize, inputSize, scale);
Wf = initializeMatrix(hiddenSize, inputSize, scale);
Wc = initializeMatrix(hiddenSize, inputSize, scale);
Wo = initializeMatrix(hiddenSize, inputSize, scale);
Ui = initializeMatrix(hiddenSize, hiddenSize, scale);
Uf = initializeMatrix(hiddenSize, hiddenSize, scale);
Uc = initializeMatrix(hiddenSize, hiddenSize, scale);
Uo = initializeMatrix(hiddenSize, hiddenSize, scale);
bi = initializeVector(hiddenSize, 0.0);
bf = initializeVector(hiddenSize, 1.0); // 遗忘门初始偏置较大
bc = initializeVector(hiddenSize, 0.0);
bo = initializeVector(hiddenSize, 0.0);
}
private double[][] initializeMatrix(int rows, int cols, double scale) {
double[][] matrix = new double[rows][cols];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = (random.nextDouble() - 0.5) * 2 * scale;
}
}
return matrix;
}
private double[] initializeVector(int size, double bias) {
double[] vec = new double[size];
Arrays.fill(vec, bias);
return vec;
}
/**
* Sigmoid 函数
*/
private double sigmoid(double x) {
return 1.0 / (1.0 + Math.exp(-x));
}
/**
* Tanh 函数
*/
private double tanh(double x) {
return Math.tanh(x);
}
/**
* 矩阵向量乘法
*/
private double[] matVecMul(double[][] mat, double[] vec) {
int m = mat.length;
int n = mat[0].length;
double[] result = new double[m];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
result[i] += mat[i][j] * vec[j];
}
}
return result;
}
/**
* 向量加法
*/
private double[] vecAdd(double[] a, double[] b) {
double[] c = new double[a.length];
for (int i = 0; i < a.length; i++) {
c[i] = a[i] + b[i];
}
return c;
}
/**
* Hadamard 积 (逐元素乘)
*/
private double[] hadamard(double[] a, double[] b) {
double[] c = new double[a.length];
for (int i = 0; i < a.length; i++) {
c[i] = a[i] * b[i];
}
return c;
}
/**
* 前向传播
* @param inputSequence 时间序列输入 [[t1], [t2], ...]
* @return 最后一个时间步的隐藏状态
*/
public double[] forward(List<double[]> inputSequence) {
double[] h = new double[hiddenSize]; // 隐藏状态
double[] c = new double[hiddenSize]; // 细胞状态
for (double[] xt : inputSequence) {
// 输入门
double[] i = matVecMul(Wi, xt);
double[] ih = matVecMul(Ui, h);
double[] it = vecAdd(i, ih);
it = vecAdd(it, bi);
double[] i_gate = Arrays.stream(it).map(this::sigmoid).toArray();
// 遗忘门
double[] f = matVecMul(Wf, xt);
double[] fh = matVecMul(Uf, h);
double[] ft = vecAdd(f, fh);
ft = vecAdd(ft, bf);
double[] f_gate = Arrays.stream(ft).map(this::sigmoid).toArray();
// 候选细胞状态
double[] g = matVecMul(Wc, xt);
double[] gh = matVecMul(Uc, h);
double[] gt = vecAdd(g, gh);
gt = vecAdd(gt, bc);
double[] cand_c = Arrays.stream(gt).map(this::tanh).toArray();
// 更新细胞状态
c = hadamard(f_gate, c); // 遗忘
c = vecAdd(c, hadamard(i_gate, cand_c)); // 输入
// 输出门
double[] o = matVecMul(Wo, xt);
double[] oh = matVecMul(Uo, h);
double[] ot = vecAdd(o, oh);
ot = vecAdd(ot, bo);
double[] o_gate = Arrays.stream(ot).map(this::sigmoid).toArray();
// 新的隐藏状态
h = hadamard(o_gate, Arrays.stream(c).map(this::tanh).toArray());
}
return h; // 返回最终隐藏状态
}
}
/**
* 基于 LSTM 的交通分类器
*/
public class LSTMClassifier {
private LSTMLayer lstm;
private double[] outputWeights; // 全连接层权重
private double outputBias;
private int sequenceLength = 6; // 使用过去6个时间步
private int inputSize; // 特征维度
private int hiddenSize = 16;
private FeatureExtractor featureExtractor;
public LSTMClassifier(FeatureExtractor featureExtractor, int inputSize) {
this.featureExtractor = featureExtractor;
this.inputSize = inputSize;
this.lstm = new LSTMLayer(inputSize, hiddenSize);
this.outputWeights = new double[hiddenSize];
this.outputBias = 0.0;
// 初始化
Random rand = new Random(42);
for (int i = 0; i < hiddenSize; i++) {
outputWeights[i] = (rand.nextDouble() - 0.5) * 0.1;
}
}
/**
* 训练(简化版,仅演示结构)
*/
public void train(List<List<TrafficSnapshot>> sequences, List<Boolean> labels) {
// 实际训练需要反向传播、梯度下降等
// 此处省略,重点展示架构
System.out.println("[LSTM] Training on " + sequences.size() + " sequences...");
// ... (真实项目应集成 DL4J 或 TensorFlow Java)
}
/**
* 预测
*/
public boolean predict(List<TrafficSnapshot> recentHistory) {
// 提取特征序列
List<double[]> featureSeq = new ArrayList<>();
for (TrafficSnapshot snap : recentHistory) {
double[] features = featureExtractor.extractFeatures(snap, null);
featureSeq.add(features);
}
// 截断或填充到固定长度
while (featureSeq.size() > sequenceLength) {
featureSeq.remove(0);
}
while (featureSeq.size() < sequenceLength) {
// 填充默认值(如零向量)
featureSeq.add(new double[inputSize]);
}
// LSTM 前向传播
double[] hiddenState = lstm.forward(featureSeq);
// 全连接 + Sigmoid
double output = outputBias;
for (int i = 0; i < hiddenState.length; i++) {
output += outputWeights[i] * hiddenState[i];
}
double prob = 1.0 / (1.0 + Math.exp(-output));
return prob >= 0.5;
}
}
五、核心分类引擎:整合一切
import java.time.LocalDateTime;
import java.util.*;
/**
* 交通模式分类引擎
* 整合数据、特征、模型,提供统一接口
*/
public class TrafficPatternClassifier {
private List<TrafficSnapshot> historicalData;
private FeatureExtractor featureExtractor;
private LogisticRegressionClassifier lrModel;
private RandomForestClassifier rfModel;
private LSTMClassifier lstmModel;
private boolean modelsTrained = false;
public TrafficPatternClassifier() {
this.historicalData = new ArrayList<>();
this.featureExtractor = new FeatureExtractor();
this.lrModel = new LogisticRegressionClassifier();
this.rfModel = new RandomForestClassifier();
// this.lstmModel = new LSTMClassifier(featureExtractor, expectedFeatureSize);
}
/**
* 添加历史数据(用于训练和特征提取)
*/
public void addHistoricalData(TrafficSnapshot snapshot) {
historicalData.add(snapshot);
// 限制历史数据大小,避免内存溢出
if (historicalData.size() > 10000) {
historicalData.remove(0);
}
}
/**
* 训练所有模型
*/
public void trainModels() {
if (historicalData.size() < 100) {
throw new IllegalStateException("Not enough data to train");
}
List<double[]> features = new ArrayList<>();
List<Boolean> labels = new ArrayList<>();
for (TrafficSnapshot snap : historicalData) {
double[] feat = featureExtractor.extractFeatures(snap, historicalData);
features.add(feat);
labels.add(snap.isPeakHour());
}
// 训练逻辑回归
lrModel.train(features, labels);
// 训练随机森林
rfModel.train(features, labels);
// 训练 LSTM (需要序列数据)
// trainLSTMModel();
modelsTrained = true;
System.out.println("All models trained successfully.");
}
/**
* 预测单个快照是否为高峰
* 使用随机森林作为主模型
*/
public ClassificationResult classify(TrafficSnapshot current) {
if (!modelsTrained) {
throw new IllegalStateException("Models not trained yet");
}
double[] features = featureExtractor.extractFeatures(current, historicalData);
boolean rfPrediction = rfModel.predict(features);
double lrProbability = lrModel.predictProbability(features);
return new ClassificationResult(
current.getTimestamp(),
current.getRoadSegmentId(),
rfPrediction,
lrProbability,
features
);
}
/**
* 分类结果封装
*/
public static class ClassificationResult {
private LocalDateTime timestamp;
private String roadSegmentId;
private boolean isPeakHour;
private double confidence; // 来自逻辑回归的概率
private double[] featuresUsed;
public ClassificationResult(LocalDateTime timestamp, String roadSegmentId,
boolean isPeakHour, double confidence, double[] featuresUsed) {
this.timestamp = timestamp;
this.roadSegmentId = roadSegmentId;
this.isPeakHour = isPeakHour;
this.confidence = confidence;
this.featuresUsed = featuresUsed;
}
// Getters...
public LocalDateTime getTimestamp() { return timestamp; }
public String getRoadSegmentId() { return roadSegmentId; }
public boolean isPeakHour() { return isPeakHour; }
public double getConfidence() { return confidence; }
public double[] getFeaturesUsed() { return featuresUsed; }
@Override
public String toString() {
return String.format("Classification{time=%s, road=%s, peak=%s, conf=%.2f}",
timestamp, roadSegmentId, isPeakHour, confidence);
}
}
}
六、数据模拟与测试
import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.util.Random;
/**
* 交通数据模拟器
* 生成逼真的交通快照流
*/
public class TrafficDataSimulator {
private Random random;
private String[] segments = {"A1", "B2", "C3", "D4"};
private LocalDateTime currentTime;
public TrafficDataSimulator() {
this.random = new Random(42);
this.currentTime = LocalDateTime.now().withMinute(0).withSecond(0).withNano(0);
}
/**
* 生成下一个时间步的快照
*/
public TrafficSnapshot nextSnapshot() {
currentTime = currentTime.plusMinutes(15); // 每15分钟一个数据点
String segment = segments[random.nextInt(segments.length)];
// 模拟高峰时段(工作日早晚高峰)
boolean isWeekday = currentTime.getDayOfWeek() != DayOfWeek.SATURDAY &&
currentTime.getDayOfWeek() != DayOfWeek.SUNDAY;
boolean isMorningPeak = isWeekday && currentTime.getHour() >= 7 && currentTime.getHour() <= 9;
boolean isEveningPeak = isWeekday && currentTime.getHour() >= 17 && currentTime.getHour() <= 19;
boolean isPeak = isMorningPeak || isEveningPeak;
// 基础速度
double baseSpeed = isPeak ? 25.0 : 50.0;
// 添加随机波动
double speed = baseSpeed + random.nextGaussian() * 10.0;
speed = Math.max(5.0, Math.min(80.0, speed)); // 限幅
int vehicleCount = (int)(speed < 30 ? 400 + random.nextInt(100) : 200 + random.nextInt(150));
double occupancyRate = 1.0 - speed / 80.0;
occupancyRate = Math.max(0.1, Math.min(0.95, occupancyRate));
WeatherCondition weather = WeatherCondition.CLEAR;
if (random.nextDouble() < 0.1) weather = WeatherCondition.RAIN;
boolean isHoliday = false;
TrafficEvent event = TrafficEvent.NONE;
if (random.nextDouble() < 0.02) event = TrafficEvent.ACCIDENT;
return new TrafficSnapshot(currentTime, segment, speed, vehicleCount,
occupancyRate, weather, isHoliday, event);
}
}
七、终极测试:一周交通模拟
/**
* 主测试类
*/
public class TrafficClassificationTest {
public static void main(String[] args) throws InterruptedException {
System.out.println("=== 交通模式分类器综合测试 ===");
TrafficDataSimulator simulator = new TrafficDataSimulator();
TrafficPatternClassifier classifier = new TrafficPatternClassifier();
// 模拟一周数据(每15分钟一个点,共 7*24*4 = 672 个点)
System.out.println("Generating one week of traffic data...");
for (int i = 0; i < 672; i++) {
TrafficSnapshot snapshot = simulator.nextSnapshot();
classifier.addHistoricalData(snapshot);
}
// 训练模型
classifier.trainModels();
// 测试最近10个样本
System.out.println("\n=== 实时预测结果 ===");
for (int i = 0; i < 10; i++) {
TrafficSnapshot current = simulator.nextSnapshot();
TrafficPatternClassifier.ClassificationResult result = classifier.classify(current);
System.out.println(result);
// 模拟实时流
Thread.sleep(100);
}
System.out.println("\n测试完成。");
}
}
八、总结:Java 的交通智能革命
通过以上深度实现,我们看到了 Java 如何从“企业胶水”蜕变为“城市大脑”:
- 完整数据闭环:从模拟 → 特征 → 模型 → 预测。
- 多模型融合:LR(可解释)、RF(鲁棒)、LSTM(时序)各司其职。
- 生产级设计:考虑内存、性能、可维护性。
- 领域知识注入:拥堵指数、历史对比等专业特征。
这不仅是分类,更是用代码理解城市心跳。
真正的智能交通,不在于预测准确率提升1%,而在于让每一次出行都更接近“零拥堵”的理想。
现在,轮到你了。试着加入深度强化学习优化信号灯,或图神经网络分析路网拓扑,让你的交通系统更进一步。
更多推荐
所有评论(0)