引言
Transformer架构自2017年提出以来,彻底改变了自然语言处理领域。与RNN和LSTM不同,Transformer完全基于注意力机制,摒弃了循环结构,实现了高效的并行计算。本文将用Java从零实现Transformer的核心组件——自注意力机制(Self-Attention)与多头注意力(Multi-Head Attention),帮助读者深入理解其内部计算流程。
核心原理
自注意力机制的核心思想是:对于序列中的每个位置,计算其与序列中所有其他位置的关联权重,然后基于这些权重对全序列信息进行加权聚合。这种”全局视野”使模型能够捕获长距离依赖关系。
多头注意力则将注意力计算拆分为多个并行的”头”,每个头学习不同的特征子空间,最终拼接所有头的输出,增强模型的表达能力。
数学表达
给定查询矩阵 Q、键矩阵 K、值矩阵 V,注意力输出为:
Attention(Q, K, V) = softmax(QK^T / √d_k) · V
其中 d_k 是键向量的维度,除以 √d_k 是为了防止点积结果过大导致softmax梯度消失。
Java实现
项目结构
src/
├── Matrix.java // 矩阵运算工具类
├── SelfAttention.java // 单头自注意力实现
├── MultiHeadAttention.java // 多头注意力实现
└── TransformerDemo.java // 演示主类
矩阵运算工具类
import java.util.Random;
/**
* 矩阵运算工具类,支持基本的矩阵操作
*/
public class Matrix {
private final double[][] data;
public final int rows;
public final int cols;
public Matrix(int rows, int cols) {
this.rows = rows;
this.cols = cols;
this.data = new double[rows][cols];
}
public Matrix(double[][] data) {
this.rows = data.length;
this.cols = data[0].length;
this.data = new double[rows][cols];
for (int i = 0; i < rows; i++) {
System.arraycopy(data[i], 0, this.data[i], 0, cols);
}
}
public void set(int row, int col, double value) {
data[row][col] = value;
}
public double get(int row, int col) {
return data[row][col];
}
/**
* 使用Xavier初始化填充矩阵
*/
public void xavierInit(Random rand) {
double scale = Math.sqrt(2.0 / (rows + cols));
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
data[i][j] = rand.nextGaussian() * scale;
}
}
}
/**
* 矩阵乘法: this * other
*/
public Matrix multiply(Matrix other) {
if (this.cols != other.rows) {
throw new IllegalArgumentException("矩阵维度不匹配");
}
Matrix result = new Matrix(this.rows, other.cols);
for (int i = 0; i < this.rows; i++) {
for (int j = 0; j < other.cols; j++) {
double sum = 0;
for (int k = 0; k < this.cols; k++) {
sum += this.data[i][k] * other.data[k][j];
}
result.data[i][j] = sum;
}
}
return result;
}
/**
* 矩阵转置
*/
public Matrix transpose() {
Matrix result = new Matrix(cols, rows);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
result.data[j][i] = data[i][j];
}
}
return result;
}
/**
* 矩阵加法
*/
public Matrix add(Matrix other) {
if (this.rows != other.rows || this.cols != other.cols) {
throw new IllegalArgumentException("矩阵维度不匹配");
}
Matrix result = new Matrix(rows, cols);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
result.data[i][j] = this.data[i][j] + other.data[i][j];
}
}
return result;
}
/**
* 按行应用softmax
*/
public Matrix softmax() {
Matrix result = new Matrix(rows, cols);
for (int i = 0; i < rows; i++) {
double max = Double.NEGATIVE_INFINITY;
for (int j = 0; j < cols; j++) {
max = Math.max(max, data[i][j]);
}
double sum = 0;
for (int j = 0; j < cols; j++) {
result.data[i][j] = Math.exp(data[i][j] - max);
sum += result.data[i][j];
}
for (int j = 0; j < cols; j++) {
result.data[i][j] /= sum;
}
}
return result;
}
/**
* 标量除法
*/
public Matrix divide(double scalar) {
Matrix result = new Matrix(rows, cols);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
result.data[i][j] = data[i][j] / scalar;
}
}
return result;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < Math.min(rows, 5); i++) {
for (int j = 0; j < Math.min(cols, 5); j++) {
sb.append(String.format("%.4f ", data[i][j]));
}
if (cols > 5) sb.append("...");
sb.append("\n");
}
if (rows > 5) sb.append("...\n");
return sb.toString();
}
}
多头注意力实现
import java.util.Random;
/**
* 多头注意力机制实现
*
* 参数说明:
* - dModel: 模型维度(输入/输出维度)
* - numHeads: 注意力头的数量
* - dK: 每个头的键/查询维度 = dModel / numHeads
*/
public class MultiHeadAttention {
private final int dModel;
private final int numHeads;
private final int dK;
private final Random rand;
// 权重矩阵: W_Q, W_K, W_V (每个头共享,也可每个头独立)
private final Matrix wq;
private final Matrix wk;
private final Matrix wv;
private final Matrix wo; // 输出投影矩阵
public MultiHeadAttention(int dModel, int numHeads) {
if (dModel % numHeads != 0) {
throw new IllegalArgumentException("dModel必须能被numHeads整除");
}
this.dModel = dModel;
this.numHeads = numHeads;
this.dK = dModel / numHeads;
this.rand = new Random(42);
// 初始化权重矩阵
this.wq = new Matrix(dModel, dModel);
this.wk = new Matrix(dModel, dModel);
this.wv = new Matrix(dModel, dModel);
this.wo = new Matrix(dModel, dModel);
wq.xavierInit(rand);
wk.xavierInit(rand);
wv.xavierInit(rand);
wo.xavierInit(rand);
}
/**
* 前向传播
* @param input [seqLen, dModel] 输入序列
* @return [seqLen, dModel] 注意力输出
*/
public Matrix forward(Matrix input) {
int seqLen = input.rows;
// 1. 线性投影得到Q, K, V
Matrix q = input.multiply(wq); // [seqLen, dModel]
Matrix k = input.multiply(wk); // [seqLen, dModel]
Matrix v = input.multiply(wv); // [seqLen, dModel]
// 2. 拆分为多个头并计算注意力
Matrix output = new Matrix(seqLen, dModel);
for (int h = 0; h < numHeads; h++) {
// 提取当前头的Q_h, K_h, V_h
Matrix qh = extractHead(q, h); // [seqLen, dK]
Matrix kh = extractHead(k, h); // [seqLen, dK]
Matrix vh = extractHead(v, h); // [seqLen, dK]
// 3. 计算注意力: softmax(Q_h * K_h^T / sqrt(dK)) * V_h
Matrix scores = qh.multiply(kh.transpose()); // [seqLen, seqLen]
scores = scores.divide(Math.sqrt(dK));
Matrix attnWeights = scores.softmax(); // [seqLen, seqLen]
Matrix headOutput = attnWeights.multiply(vh); // [seqLen, dK]
// 4. 将头的输出拼接回完整维度
mergeHead(output, headOutput, h);
}
// 5. 最终线性投影
return output.multiply(wo);
}
/**
* 从拼接的Q/K/V中提取指定头的矩阵
*/
private Matrix extractHead(Matrix m, int headIdx) {
int seqLen = m.rows;
Matrix head = new Matrix(seqLen, dK);
int startCol = headIdx * dK;
for (int i = 0; i < seqLen; i++) {
for (int j = 0; j < dK; j++) {
head.set(i, j, m.get(i, startCol + j));
}
}
return head;
}
/**
* 将单头输出合并到最终输出矩阵中
*/
private void mergeHead(Matrix output, Matrix head, int headIdx) {
int seqLen = output.rows;
int startCol = headIdx * dK;
for (int i = 0; i < seqLen; i++) {
for (int j = 0; j < dK; j++) {
double val = output.get(i, startCol + j) + head.get(i, j);
output.set(i, startCol + j, val);
}
}
}
}
演示主类
/**
* Transformer注意力机制演示
*/
public class TransformerDemo {
public static void main(String[] args) {
// 模拟参数: 序列长度4, 模型维度8, 4个注意力头
int seqLen = 4;
int dModel = 8;
int numHeads = 4;
// 构建随机输入 [seqLen, dModel]
Matrix input = new Matrix(seqLen, dModel);
Random rand = new Random(123);
for (int i = 0; i < seqLen; i++) {
for (int j = 0; j < dModel; j++) {
input.set(i, j, rand.nextGaussian() * 0.1);
}
}
System.out.println("输入矩阵 (前5x5预览):");
System.out.println(input);
// 创建多头注意力层并前向传播
MultiHeadAttention mha = new MultiHeadAttention(dModel, numHeads);
Matrix output = mha.forward(input);
System.out.println("多头注意力输出 (前5x5预览):");
System.out.println(output);
// 验证输出维度
System.out.println("输入维度: " + input.rows + "x" + input.cols);
System.out.println("输出维度: " + output.rows + "x" + output.cols);
System.out.println("✓ 维度检查通过,注意力机制运行正常");
}
}
复杂度分析
对于序列长度 n、模型维度 d、头数 h 的多头注意力:
| 操作 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| Q/K/V投影 | O(n · d²) | O(n · d) |
| 注意力计算(每头) | O(n² · d/h) | O(n²) |
| 输出投影 | O(n · d²) | O(n · d) |
| 总计 | O(n² · d + n · d²) | O(n² + n · d) |
其中 O(n²) 是注意力权重的计算开销,这是Transformer的主要瓶颈。对于长序列,可使用稀疏注意力、线性注意力或Flash Attention等优化方案。
总结
本文用Java完整实现了Transformer的多头注意力机制,包括矩阵运算、分头计算、softmax归一化与输出投影。读者可以基于此扩展实现完整Transformer(加入位置编码、前馈网络、层归一化与残差连接),进一步探索BERT、GPT等预训练模型的原理。