#P3719. 第3题-数据中心水温调节档位决策
-
1000ms
Tried: 1659
Accepted: 56
Difficulty: 6
所属公司 :
华为
时间 :2025年9月18日(留学生)-AI岗
-
算法标签>机器学习算法
第3题-数据中心水温调节档位决策
模型与目标
-
线性部分:O=XW+b,其中 X∈RN×n、W∈Rn×k、b∈R1×k。
-
概率:P=softmax(O),对每行做
(减去按行最大值做数值稳定)。 -
损失(带 L2 正则):

-
梯度:

-
预测:对每个样本取 argmaxjpij 得到分类 0∼k−1。
C++
#include <bits/stdc++.h>
using namespace std;
// 说明:实现一个多分类 softmax 回归,使用全批量梯度下降与L2正则。
// 输入:n, k, 每类样本数(k个),m;随后 N 行训练样本、m 行待预测样本
// 输出:m 行,每行一个预测类别编号
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, k;
if (!(cin >> n >> k)) return 0;
vector<int> cnt(k);
long long N = 0;
for (int i = 0; i < k; ++i) { cin >> cnt[i]; N += cnt[i]; }
int m; cin >> m;
// 读取训练数据
vector<vector<double>> X_train(N, vector<double>(n, 0.0));
for (long long i = 0; i < N; ++i) {
for (int j = 0; j < n; ++j) cin >> X_train[i][j];
}
// 生成标签(按类别顺序)
vector<int> y_train;
y_train.reserve(N);
for (int c = 0; c < k; ++c) {
for (int t = 0; t < cnt[c]; ++t) y_train.push_back(c);
}
// 读取待预测数据
vector<vector<double>> X_test(m, vector<double>(n, 0.0));
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) cin >> X_test[i][j];
}
// 特征标准化:计算均值和标准差(仅训练集)
const double eps = 1e-8;
vector<double> mu(n, 0.0), sigma(n, 0.0);
for (int j = 0; j < n; ++j) {
double s = 0.0;
for (long long i = 0; i < N; ++i) s += X_train[i][j];
mu[j] = (N ? s / N : 0.0);
}
for (int j = 0; j < n; ++j) {
double ss = 0.0;
for (long long i = 0; i < N; ++i) {
double d = X_train[i][j] - mu[j];
ss += d * d;
}
sigma[j] = sqrt((N ? ss / max(1LL, N - 1) : 0.0));
if (sigma[j] < eps) sigma[j] = 1.0; // 方差过小则置1
}
auto standardize = [&](vector<vector<double>>& X) {
for (auto &row : X) {
for (int j = 0; j < n; ++j) row[j] = (row[j] - mu[j]) / (sigma[j] + eps);
}
};
standardize(X_train);
standardize(X_test);
// 参数:W (n x k), b (k)
vector<vector<double>> W(n, vector<double>(k, 0.0));
vector<double> b(k, 0.0);
// 训练超参
int epochs = 600; // 训练轮数
double lr = 0.1; // 学习率
double reg = 1e-4; // L2 正则
// 训练
vector<double> logits(k), probs(k);
for (int epoch = 0; epoch < epochs; ++epoch) {
// 梯度清零
vector<vector<double>> dW(n, vector<double>(k, 0.0));
vector<double> db(k, 0.0);
// 全批量:逐样本累积梯度
for (long long i = 0; i < N; ++i) {
// 计算 logits = X_i * W + b
for (int j = 0; j < k; ++j) {
double s = b[j];
for (int f = 0; f < n; ++f) s += X_train[i][f] * W[f][j];
logits[j] = s;
}
// softmax(数值稳定:减最大值)
double mx = *max_element(logits.begin(), logits.end());
double sumExp = 0.0;
for (int j = 0; j < k; ++j) { probs[j] = exp(logits[j] - mx); sumExp += probs[j]; }
for (int j = 0; j < k; ++j) probs[j] /= (sumExp + eps);
// 残差 = p - y_onehot
int y = y_train[i];
for (int j = 0; j < k; ++j) {
double diff = probs[j] - (j == y ? 1.0 : 0.0);
// 累积 dW 与 db
for (int f = 0; f < n; ++f) dW[f][j] += X_train[i][f] * diff;
db[j] += diff;
}
}
// 平均并加正则
double invN = (N ? 1.0 / (double)N : 1.0);
for (int f = 0; f < n; ++f) {
for (int j = 0; j < k; ++j) {
dW[f][j] = dW[f][j] * invN + reg * W[f][j];
}
}
for (int j = 0; j < k; ++j) db[j] *= invN;
// 参数更新
for (int f = 0; f < n; ++f)
for (int j = 0; j < k; ++j)
W[f][j] -= lr * dW[f][j];
for (int j = 0; j < k; ++j) b[j] -= lr * db[j];
// 可选:学习率轻微衰减(稳定训练)
if ((epoch + 1) % 150 == 0) lr *= 0.9;
}
// 预测
for (int i = 0; i < m; ++i) {
for (int j = 0; j < k; ++j) {
double s = b[j];
for (int f = 0; f < n; ++f) s += X_test[i][f] * W[f][j];
logits[j] = s;
}
int argmax = 0;
for (int j = 1; j < k; ++j) if (logits[j] > logits[argmax]) argmax = j;
cout << argmax << "\n";
}
return 0;
}
Python
# 说明:多分类 softmax 回归,使用全批量梯度下降与L2正则
# 输入输出同题面描述
import sys
import math
def read_ints():
return list(map(float, sys.stdin.readline().strip().split()))
# 为了稳妥处理空白行,使用迭代读取
tokens = []
for line in sys.stdin:
parts = line.strip().split()
if parts:
tokens.extend(parts)
it = iter(tokens)
def next_int():
return int(next(it))
def next_float():
return float(next(it))
n = next_int()
k = next_int()
cnt = [next_int() for _ in range(k)]
N = sum(cnt)
m = next_int()
# 读取训练与预测
X_train = [[next_float() for _ in range(n)] for _ in range(N)]
y_train = []
for c in range(k):
for _ in range(cnt[c]):
y_train.append(c)
X_test = [[next_float() for _ in range(n)] for _ in range(m)]
# 标准化
eps = 1e-8
mu = [0.0]*n
sigma = [0.0]*n
for j in range(n):
s = sum(X_train[i][j] for i in range(N)) if N > 0 else 0.0
mu[j] = s / N if N > 0 else 0.0
for j in range(n):
ss = 0.0
for i in range(N):
d = X_train[i][j] - mu[j]
ss += d*d
sigma[j] = math.sqrt((ss / max(1, N-1))) if N > 1 else 1.0
if sigma[j] < eps: sigma[j] = 1.0
def standardize(X):
for i in range(len(X)):
row = X[i]
for j in range(n):
row[j] = (row[j] - mu[j]) / (sigma[j] + eps)
standardize(X_train)
standardize(X_test)
# 参数
W = [[0.0]*k for _ in range(n)]
b = [0.0]*k
# 超参
epochs = 600
lr = 0.1
reg = 1e-4
# 训练
for epoch in range(epochs):
dW = [[0.0]*k for _ in range(n)]
db = [0.0]*k
for i in range(N):
# logits
logits = [b[j] + sum(X_train[i][f]*W[f][j] for f in range(n)) for j in range(k)]
mx = max(logits)
exps = [math.exp(v - mx) for v in logits]
s = sum(exps) + eps
probs = [v/s for v in exps]
y = y_train[i]
for j in range(k):
diff = probs[j] - (1.0 if j == y else 0.0)
for f in range(n):
dW[f][j] += X_train[i][f] * diff
db[j] += diff
invN = (1.0 / N) if N > 0 else 1.0
for f in range(n):
for j in range(k):
dW[f][j] = dW[f][j] * invN + reg * W[f][j]
for j in range(k):
db[j] *= invN
for f in range(n):
for j in range(k):
W[f][j] -= lr * dW[f][j]
for j in range(k):
b[j] -= lr * db[j]
if (epoch + 1) % 150 == 0:
lr *= 0.9 # 轻微衰减
# 预测
out_lines = []
for i in range(m):
logits = [b[j] + sum(X_test[i][f]*W[f][j] for f in range(n)) for j in range(k)]
argmax = max(range(k), key=lambda j: logits[j])
out_lines.append(str(argmax))
print("\n".join(out_lines))
Java
import java.io.*;
import java.util.*;
// 说明:多分类 softmax 回归,使用全批量梯度下降与L2正则
public class Main {
static class FastScanner {
private final InputStream in;
private final byte[] buffer = new byte[1 << 16];
private int ptr = 0, len = 0;
FastScanner(InputStream is) { in = is; }
private int read() throws IOException {
if (ptr >= len) {
len = in.read(buffer);
ptr = 0;
if (len <= 0) return -1;
}
return buffer[ptr++];
}
String next() throws IOException {
StringBuilder sb = new StringBuilder();
int c;
while ((c = read()) <= ' ' && c != -1) ;
if (c == -1) return null;
do { sb.append((char)c); c = read(); } while (c > ' ');
return sb.toString();
}
int nextInt() throws IOException { return Integer.parseInt(next()); }
double nextDouble() throws IOException { return Double.parseDouble(next()); }
}
public static void main(String[] args) throws Exception {
FastScanner fs = new FastScanner(System.in);
String token = fs.next();
if (token == null) return;
int n = Integer.parseInt(token);
int k = Integer.parseInt(fs.next());
int[] cnt = new int[k];
long N = 0;
for (int i = 0; i < k; i++) { cnt[i] = Integer.parseInt(fs.next()); N += cnt[i]; }
int m = Integer.parseInt(fs.next());
double[][] Xtrain = new double[(int)N][n];
for (int i = 0; i < N; i++) {
for (int j = 0; j < n; j++) Xtrain[i][j] = fs.nextDouble();
}
int[] y = new int[(int)N];
int idx = 0;
for (int c = 0; c < k; c++) {
for (int t = 0; t < cnt[c]; t++) y[idx++] = c;
}
double[][] Xtest = new double[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) Xtest[i][j] = fs.nextDouble();
}
// 标准化
double eps = 1e-8;
double[] mu = new double[n];
double[] sigma = new double[n];
Arrays.fill(mu, 0.0);
Arrays.fill(sigma, 0.0);
for (int j = 0; j < n; j++) {
double s = 0.0;
for (int i = 0; i < N; i++) s += Xtrain[i][j];
mu[j] = (N > 0) ? s / N : 0.0;
}
for (int j = 0; j < n; j++) {
double ss = 0.0;
for (int i = 0; i < N; i++) {
double d = Xtrain[i][j] - mu[j];
ss += d * d;
}
sigma[j] = (N > 1) ? Math.sqrt(ss / Math.max(1, (int)N - 1)) : 1.0;
if (sigma[j] < eps) sigma[j] = 1.0;
}
standardizeInPlace(Xtrain, mu, sigma, eps);
standardizeInPlace(Xtest, mu, sigma, eps);
// 参数
double[][] W = new double[n][k];
double[] b = new double[k];
// 超参
int epochs = 600;
double lr = 0.1;
double reg = 1e-4;
// 训练
double[][] dW = new double[n][k];
double[] db = new double[k];
double[] logits = new double[k];
double[] probs = new double[k];
for (int epoch = 0; epoch < epochs; epoch++) {
// 梯度清零
for (int f = 0; f < n; f++) Arrays.fill(dW[f], 0.0);
Arrays.fill(db, 0.0);
for (int i = 0; i < N; i++) {
// logits
for (int j = 0; j < k; j++) {
double s = b[j];
for (int f = 0; f < n; f++) s += Xtrain[i][f] * W[f][j];
logits[j] = s;
}
// softmax(数值稳定)
double mx = logits[0];
for (int j = 1; j < k; j++) if (logits[j] > mx) mx = logits[j];
double sumExp = 0.0;
for (int j = 0; j < k; j++) {
probs[j] = Math.exp(logits[j] - mx);
sumExp += probs[j];
}
sumExp += eps;
for (int j = 0; j < k; j++) probs[j] /= sumExp;
int yy = y[i];
for (int j = 0; j < k; j++) {
double diff = probs[j] - (j == yy ? 1.0 : 0.0);
for (int f = 0; f < n; f++) dW[f][j] += Xtrain[i][f] * diff;
db[j] += diff;
}
}
double invN = (N > 0) ? (1.0 / N) : 1.0;
for (int f = 0; f < n; f++) {
for (int j = 0; j < k; j++) {
dW[f][j] = dW[f][j] * invN + reg * W[f][j];
}
}
for (int j = 0; j < k; j++) db[j] *= invN;
for (int f = 0; f < n; f++)
for (int j = 0; j < k; j++)
W[f][j] -= lr * dW[f][j];
for (int j = 0; j < k; j++) b[j] -= lr * db[j];
if ((epoch + 1) % 150 == 0) lr *= 0.9; // 轻微衰减
}
// 预测
StringBuilder sb = new StringBuilder();
for (int i = 0; i < m; i++) {
for (int j = 0; j < k; j++) {
double s = b[j];
for (int f = 0; f < n; f++) s += Xtest[i][f] * W[f][j];
logits[j] = s;
}
int argmax = 0;
for (int j = 1; j < k; j++) if (logits[j] > logits[argmax]) argmax = j;
sb.append(argmax).append('\n');
}
System.out.print(sb.toString());
}
static void standardizeInPlace(double[][] X, double[] mu, double[] sigma, double eps) {
int rows = X.length, n = mu.length;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < n; j++) {
X[i][j] = (X[i][j] - mu[j]) / (sigma[j] + eps);
}
}
}
}
题目内容
数据中心的机房需要散热,一般通过水冷系统调控机房的温度。出于节能的目的,需要收集数据中心最近一段时间的业务负裁,同时结合历史信息,外界气温、湿度等因素调高或调低冷机的出水温度。
逻辑回归是比轻量的模型,其输出 0 或 1 可以分别表示调低或调高温度。
在实际使用场景中,仅判断调低或调高不能满足业务要求,需要细化到调低 1 度、调高 0.5度、维持不变、调高 0.5 度、调高 1 度等不同的"档位"。
因此,可以对逻辑回归进行改造,将其输出更改为 softmax 可满足要求。
请根据提供的观测数据,训练改造后的模型,并根据给定样本数据预测调节的档位。
优化后的模型:O=XW+b,P=softmax(O)。
X∈R(m,n) 表示 m 条样本,每个样本有 n 个特征;
W∈R(n,k) 为权重;k 是档位数(即 k 个分类);
b∈R(1,k) 为偏置。W 和 b 通过训练得到。
P∈R(m,k) 表示预测的概率,取概率最大的作为输出档位。
输入描述
1.第一行是数据 schema,分别表示特征数 n ,分类数 k ,第 0 类样本数(即档位 0 ),第 1 类样本数,…,第 k−1 类样本数,待预测样本数 m ;数据均为 int 类型
2.后续的多行是 k 个分类的训练样本(按照分类 0 的多条样本、分类 1 的多条样本 、… 依次排列,一行一个样本)和 m 条待预测样本(一行个样本);数据均为 float 类型
输出描述
每个待预测样本所所属的分类,一行输出一个样本的预测结果
样例1
输入
2 3 2 3 2 3
9 95
33 53
53 55
69 21
68 31
70 85
80 83
25 70
45 30
79 86
输出
0
1
2
说明
1.第一行数据 2 3 2 3 2 3 ,表示每条样本 2 个特征,共有 3 个分类;分类 0 样本 2 条,分类 1 样本 3 条,分类 2 样本 2 条;测试用例 3 条
2.第 2~3 行为分类 0 样本(标签为 0 ),4~6 行为分类 1 样本(标签 1 ),7~8 行为分类 2 样本(标签为 2 )
3.最后 3 行是待预测样本
输出:3 个待预测样本的预测结果为分类 0 、分类 1 、分类 2
样例2
输入
3 3 4 3 4 5
9 95 7
33 53 13
45 43 6
40 50 11
53 55 36
69 21 55
68 31 43
70 85 23
80 83 46
70 73 55
76 78 53
25 70 6
20 69 16
45 30 50
79 86 51
70 76 36
输出
0
0
1
2
2
说明
1、第一行数据 3 3 4 3 4 5 ,表示每条样本 3 个特征,共有 3 个分类;分类 0 样本 4 条,分类 1 样本 3 条,分类 2 样本 4 条;测试用例 5 条
2.第 2~5 行为分类 0 样本(标签为 0),6~8 行为分类 1 样本(标签 1 ),9~12 行为分类 2 样本(标签为 2 )
3.最后 5 行是待预测样本
输出:
5 个待预测样本的预测结果为分类 0 (即档位 0 )、分类 0 、分类 1 、分类 2 、分类 2
提示
1.使用交叉熵损失函数,标签值需转为 one−hot 编码;梯度求解函数如下方公式所示,Y 为真实标签(即 one−hot 编码);P 为预测的概率(即 softmax 的输出)
2.采用批梯度下降优化参数;选择比较好的学习率(根据经验,为了加快收敛速度,初始学习率可设置较大的数值,例如 5 );适当增加迭代次数有助于获得更精确的结果;
3.原始数据值域一般在 100 以内,为避免计算 softmax 越界,需要对数据集执行归一化操作。
交叉熵函数
$-\frac{1}{m} \sum_{i=1}^{m} \sum_{j=1}^{k} Y_{i, j} \log \left(P_{i, j}\right)$
损失函数对 W 的梯度
m1XT(P−Y)
损失函数对 b 的梯度
$\frac{1}{m} \sum_{i=1}^{m}\left(P_{i,:}-Y_{i,:}\right)$