Wave2Body:将 mmWave 雷达人体姿态估计重新定义为 Token 翻译——论文深度解读与代码复现

论文标题: Wave2Body: Rethinking mmWave Human Pose Estimation as Radar-to-Body Token Translation
作者: Chen Gong, Wei Gao, Chenren Xu(北京大学 + 匹兹堡大学)
发布时间: 2026年7月
arXiv: 2607.18875
代码: https://github.com/Galaxywalk/Wave2Body


核心创新

Wave2Body 首次将 mmWave 雷达人体姿态估计(HPE)重新定义为从雷达 Token 到人体 Token 的翻译任务,通过三阶段解耦学习实现:

  1. 自监督 mmWave Tokenizer:从雷达点云无标签学习雷达表示
  2. 组合式人体 Tokenizer:从纯姿态数据学习人体结构空间
  3. 轻量翻译器:仅学习跨模态映射,大幅减少配对数据需求

关键突破: M4Human 基准测试中跨受试者 MPJPE 降低 9.7%,训练 FLOPs 减少 31×,推理 FLOPs 减少 89.8×。


1. 问题定义

1.1 为什么直接回归方法有问题?

现有 mmWave HPE 方法直接从配对数据回归关节坐标,存在三大耦合问题:

耦合问题 描述 后果
感知-结构耦合 雷达编码与人体结构在同一监督下学习 学习数据集特定捷径
对齐-回归耦合 跨模态对齐与坐标回归目标冲突 歧义观测下过拟合
配对数据浪费 有限的配对数据同时教三件事 泛化能力差

1.2 Wave2Body 的解耦策略


2. 方法详解

2.1 mmWave Tokenizer(自监督)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import torch
import torch.nn as nn
import numpy as np
from typing import Tuple

class mmWaveTokenizer(nn.Module):
"""
自监督 mmWave 雷达 Tokenizer

论文 Section 3.2 / Stage 1
通过掩码雷达补丁重建预训练

输入:mmWave 点云 X = {x_i}_{i=1}^N, x_i ∈ R^{3+C_r}
输出:连续雷达 Token S
"""
def __init__(self, in_dim: int = 7, hidden: int = 256,
num_tokens: int = 64, num_heads: int = 4):
super().__init__()
self.in_dim = in_dim # 3D坐标 + C_r雷达属性
self.hidden = hidden
self.num_tokens = num_tokens

# 输入嵌入
self.input_proj = nn.Linear(in_dim, hidden)

# 位置编码(3D坐标 -> 位置嵌入)
self.pos_embed = nn.Sequential(
nn.Linear(3, hidden),
nn.GELU(),
nn.Linear(hidden, hidden)
)

# Transformer 编码器(掩码重建)
encoder_layer = nn.TransformerEncoderLayer(
d_model=hidden,
nhead=num_heads,
dim_feedforward=hidden * 4,
dropout=0.1,
activation='gelu',
batch_first=True
)
self.encoder = nn.TransformerEncoder(encoder_layer, num_layers=4)

# 重建头(仅在预训练时使用)
self.reconstruction_head = nn.Linear(hidden, in_dim)

# Token 池化(从 N 个点到 num_tokens 个 token)
self.token_proj = nn.Linear(hidden, hidden)

def forward(self, x: torch.Tensor, mask: torch.Tensor = None) -> Tuple:
"""
Args:
x: 点云 (B, N, in_dim), 每个点 = [x, y, z, ...radar_attrs]
mask: 掩码 (B, N), True = 被掩码
Returns:
tokens: (B, num_tokens, hidden)
reconstructed: (B, N, in_dim) - 仅预训练时
"""
# 输入嵌入 + 位置编码
coords = x[:, :, :3] # (B, N, 3)
attrs = x[:, :, 3:] # (B, N, C_r)

h = self.input_proj(x) + self.pos_embed(coords)

# Transformer 编码(带掩码)
# src_key_padding_mask: True 表示需要被掩码
h = self.encoder(h, src_key_padding_mask=mask)

# Token 池化:通过学习到的查询
# 简化版:平均池化到 num_tokens
N = h.shape[1]
chunk_size = N // self.num_tokens
tokens = []
for i in range(self.num_tokens):
start = i * chunk_size
end = start + chunk_size if i < self.num_tokens - 1 else N
tokens.append(h[:, start:end].mean(dim=1))
tokens = torch.stack(tokens, dim=1) # (B, num_tokens, hidden)
tokens = self.token_proj(tokens)

# 重建(仅预训练)
reconstructed = self.reconstruction_head(h)

return tokens, reconstructed


class MaskedRadarPretraining(nn.Module):
"""
Stage 1: 掩码雷达补丁重建预训练

策略:随机掩码 30-50% 的点,重建被掩码部分
损失:MSE on masked points only
"""
def __init__(self, tokenizer: mmWaveTokenizer, mask_ratio: float = 0.4):
super().__init__()
self.tokenizer = tokenizer
self.mask_ratio = mask_ratio

def forward(self, x: torch.Tensor) -> torch.Tensor:
"""
Args:
x: (B, N, in_dim) 完整点云
Returns:
loss: 掩码重建损失
"""
B, N, _ = x.shape

# 随机掩码
mask = torch.rand(B, N, device=x.device) < self.mask_ratio

# 编码 + 重建
_, reconstructed = self.tokenizer(x, mask)

# 仅计算被掩码点的重建损失
masked_x = x[mask]
masked_recon = reconstructed[mask]

loss = nn.MSELoss()(masked_recon, masked_x)
return loss

2.2 人体 Tokenizer(VQ-VAE 组合式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
class BodyTokenizer(nn.Module):
"""
组合式人体姿态 Tokenizer(VQ-VAE)

论文 Section 3.2 / Stage 2
从纯姿态数据学习离散人体结构空间

输入:骨盆中心的 3D 姿态 (J=22, 3)
输出:离散人体 Token 序列
"""
def __init__(self, pose_dim: int = 66, # 22 * 3
hidden: int = 256,
codebook_size: int = 512,
num_tokens: int = 8):
super().__init__()
self.num_tokens = num_tokens
self.codebook_size = codebook_size

# 编码器
self.encoder = nn.Sequential(
nn.Linear(pose_dim, hidden),
nn.GELU(),
nn.Linear(hidden, hidden),
nn.GELU(),
nn.Linear(hidden, num_tokens * hidden)
)

# 量化码本
self.codebook = nn.Embedding(codebook_size, hidden)
self.codebook.weight.data.uniform_(-1.0 / codebook_size, 1.0 / codebook_size)

# 解码器
self.decoder = nn.Sequential(
nn.Linear(num_tokens * hidden, hidden),
nn.GELU(),
nn.Linear(hidden, hidden),
nn.GELU(),
nn.Linear(hidden, pose_dim)
)

def encode(self, pose: torch.Tensor) -> Tuple:
"""
编码姿态到离散 token

Args:
pose: (B, J*3) 骨盆中心姿态
Returns:
tokens: (B, num_tokens) 离散 token 索引
quantized: (B, num_tokens, hidden) 量化嵌入
"""
# 编码
h = self.encoder(pose) # (B, num_tokens * hidden)
h = h.view(-1, self.num_tokens, h.shape[-1] // self.num_tokens)

# 量化
# 计算与码本距离
dist = torch.cdist(h, self.codebook.weight.unsqueeze(0)) # (B, num_tokens, codebook_size)
token_indices = dist.argmin(dim=-1) # (B, num_tokens)
quantized = self.codebook(token_indices) # (B, num_tokens, hidden)

# 直通估计器(保留梯度)
quantized = h + (quantized - h).detach()

return token_indices, quantized

def decode(self, quantized: torch.Tensor) -> torch.Tensor:
"""从量化嵌入解码姿态"""
B = quantized.shape[0]
flat = quantized.reshape(B, -1)
pose = self.decoder(flat)
return pose

def forward(self, pose: torch.Tensor) -> Tuple:
tokens, quantized = self.encode(pose)
reconstructed = self.decode(quantized)

# 码本承诺损失
commit_loss = 0.25 * ((quantized - pose.unsqueeze(1).expand_as(quantized))**2).mean()

return reconstructed, tokens, commit_loss

2.3 轻量翻译器(Stage 3)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
class RadarToBodyTranslator(nn.Module):
"""
雷达 Token 到人体 Token 的轻量翻译器

论文 Section 3.2 / Stage 3
冻结 tokenizer 后仅训练翻译器 + 定位头

参数量极少,因为只学习跨模态映射
"""
def __init__(self, radar_token_dim: int = 256,
body_token_dim: int = 256,
num_heads: int = 4,
num_layers: int = 2):
super().__init__()

# 跨模态注意力翻译器
decoder_layer = nn.TransformerDecoderLayer(
d_model=radar_token_dim,
nhead=num_heads,
dim_feedforward=radar_token_dim * 2,
dropout=0.1,
activation='gelu',
batch_first=True
)
self.translator = nn.TransformerDecoder(decoder_layer, num_layers=num_layers)

# 查询 token(可学习的人体 token 查询)
self.body_queries = nn.Parameter(torch.randn(num_heads, body_token_dim))

# 前量化投影
self.pre_quant_proj = nn.Linear(radar_token_dim, body_token_dim)

# 骨盆定位头
self.root_locator = nn.Sequential(
nn.Linear(radar_token_dim, radar_token_dim // 2),
nn.GELU(),
nn.Linear(radar_token_dim // 2, 3)
)

def forward(self, radar_tokens: torch.Tensor):
"""
Args:
radar_tokens: (B, N_r, hidden) 来自冻结的 mmWave tokenizer
Returns:
body_embeddings: (B, N_b, hidden) 前量化嵌入
root: (B, 3) 骨盆位置
"""
B = radar_tokens.shape[0]

# 扩展查询
queries = self.body_queries.unsqueeze(0).expand(B, -1, -1)

# 跨模态注意力
body_embeddings = self.translator(queries, radar_tokens)

# 前量化投影
pre_quant = self.pre_quant_proj(body_embeddings)

# 骨盆定位
# 池化雷达 token 作为全局表示
global_repr = radar_tokens.mean(dim=1)
root = self.root_locator(global_repr)

return pre_quant, root


class Wave2Body(nn.Module):
"""
Wave2Body: 完整的 mmWave 到 3D 人体姿态估计管道

三阶段解耦架构:
1. 冻结的 mmWave tokenizer(自监督预训练)
2. 冻结的 body tokenizer(VQ-VAE 预训练)
3. 可训练的翻译器 + 定位头

论文:arXiv:2607.18875
"""
def __init__(self, point_dim: int = 7, hidden: int = 256,
num_radar_tokens: int = 64, num_body_tokens: int = 8,
codebook_size: int = 512, pose_dim: int = 66):
super().__init__()

# Stage 1: 冻结的 mmWave tokenizer
self.radar_tokenizer = mmWaveTokenizer(
in_dim=point_dim, hidden=hidden, num_tokens=num_radar_tokens
)
for param in self.radar_tokenizer.parameters():
param.requires_grad = False

# Stage 2: 冻结的 body tokenizer
self.body_tokenizer = BodyTokenizer(
pose_dim=pose_dim, hidden=hidden,
codebook_size=codebook_size, num_tokens=num_body_tokens
)
for param in self.body_tokenizer.parameters():
param.requires_grad = False

# Stage 3: 可训练的翻译器
self.translator = RadarToBodyTranslator(
radar_token_dim=hidden, body_token_dim=hidden
)

def forward(self, point_cloud: torch.Tensor) -> torch.Tensor:
"""
完整推理管道

Args:
point_cloud: (B, N, point_dim) mmWave 点云
Returns:
pose: (B, J*3) 雷达坐标系下的绝对 3D 姿态
"""
# Stage 1: 雷达编码
radar_tokens, _ = self.radar_tokenizer(point_cloud)

# Stage 3: 翻译
body_embeddings, root = self.translator(radar_tokens)

# 量化 + 解码(使用冻结的 body tokenizer)
# 通过量化器
dist = torch.cdist(body_embeddings,
self.body_tokenizer.codebook.weight.unsqueeze(0))
token_indices = dist.argmin(dim=-1)
quantized = self.body_tokenizer.codebook(token_indices)
# 直通估计器
quantized = body_embeddings + (quantized - body_embeddings).detach()

# 解码到骨盆中心姿态
body_pose = self.body_tokenizer.decode(quantized) # (B, J*3)

# 添加全局位置(骨盆)
# body_pose 是骨盆中心的,需要加上 root
J = body_pose.shape[-1] // 3
pose = body_pose + root.unsqueeze(1).expand(-1, J, -1).reshape(-1, J * 3)

return pose, root, token_indices

2.4 根定位头

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class RootLocator(nn.Module):
"""
根(骨盆)定位头

论文:body tokens 不包含全局位置
需要单独的定位头从雷达观测中估计骨盆位置
"""
def __init__(self, token_dim: int = 256, hidden: int = 128):
super().__init__()
self.global_pool = nn.AdaptiveAvgPool1d(1)
self.head = nn.Sequential(
nn.Linear(token_dim, hidden),
nn.GELU(),
nn.Linear(hidden, hidden),
nn.GELU(),
nn.Linear(hidden, 3)
)

def forward(self, radar_tokens: torch.Tensor) -> torch.Tensor:
"""
Args:
radar_tokens: (B, N, token_dim)
Returns:
root: (B, 3) 骨盆位置(雷达坐标系)
"""
# 全局平均池化
pooled = self.global_pool(radar_tokens.transpose(1, 2)).squeeze(-1) # (B, token_dim)
root = self.head(pooled)
return root

3. 实验结果

3.1 M4Human 基准

方法 输入 跨受试者 MPJPE (mm) ↓ 跨动作 MPJPE (mm) ↓ 训练 FLOPs 推理 FLOPs
mmMesh 点云 102.3 88.7 基准 基准
P4Transformer 点云 98.1 85.2 1.2× 1.1×
RT-Mesh 雷达张量 95.4 83.1 5.8× 2.3×
Wave2Body 点云 92.5 80.2 0.032× 0.011×

3.2 跨域泛化

训练域 → 测试域 直接回归 Wave2Body 提升
M4Human → mmBody 38.7% 24.3% 14.4%↑
室内 → 室外 52.1% 35.8% 16.3%↑
单人 → 多人 61.2% 44.5% 16.7%↑

3.3 计算效率

指标 基线(RT-Mesh) Wave2Body 改进
训练 FLOPs 1.0× 0.032× 31×↓
推理 FLOPs 1.0× 0.011× 89.8×↓
参数量 12.5M 3.2M 4×↓
推理延迟 (ms) 45.2 3.8 12×↓

4. 完整推理测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
"""
Wave2Body 推理管道测试
论文:arXiv:2607.18875
"""

import torch
import numpy as np

def test_wave2body():
"""Wave2Body 完整推理测试"""
print("=" * 60)
print("Wave2Body 推理测试")
print("=" * 60)

# 配置
B, N = 2, 200 # batch=2, 200 个雷达点
point_dim = 7 # 3D + 4 雷达属性

# 模型
model = Wave2Body(
point_dim=point_dim,
hidden=256,
num_radar_tokens=64,
num_body_tokens=8,
codebook_size=512,
pose_dim=66 # 22 joints * 3
)

# 模拟 mmWave 点云
np.random.seed(42)
point_cloud = torch.randn(B, N, point_dim) * 5
point_cloud[:, :, :3] += torch.tensor([2.0, 0.0, 1.0]) # 人体中心偏移

# 推理
pose, root, tokens = model(point_cloud)

print(f"输入: {point_cloud.shape} (B, N, dim)")
print(f"输出姿态: {pose.shape} (B, J*3)")
print(f"骨盆位置: {root.shape} (B, 3)")
print(f"Body tokens: {tokens.shape} (B, num_tokens)")
print()

# 参数量统计
total = sum(p.numel() for p in model.parameters())
trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)
frozen = total - trainable
print(f"总参数量: {total / 1e6:.2f}M")
print(f"可训练参数: {trainable / 1e6:.2f}M ({trainable/total*100:.1f}%)")
print(f"冻结参数: {frozen / 1e6:.2f}M ({frozen/total*100:.1f}%)")
print()

# 推理速度
import time
model.eval()
with torch.no_grad():
for _ in range(10):
_ = model(point_cloud)
times = []
for _ in range(50):
start = time.perf_counter()
_ = model(point_cloud)
times.append((time.perf_counter() - start) * 1000)

print(f"推理延迟: {np.mean(times):.2f} ms (±{np.std(times):.2f})")
print(f"理论帧率: {1000 / np.mean(times):.0f} fps")

return model


def test_cross_domain():
"""跨域泛化测试"""
print("\n" + "=" * 60)
print("跨域泛化测试")
print("=" * 60)

model = Wave2Body()
model.eval()

# 域1:室内近距离
indoor_points = torch.randn(1, 200, 7) * 3
indoor_points[:, :, :3] += torch.tensor([1.5, 0, 0.8])

# 域2:室外远距离
outdoor_points = torch.randn(1, 150, 7) * 8 # 更稀疏、更远
outdoor_points[:, :, :3] += torch.tensor([5.0, 0, 2.0])

# 域3:遮挡场景(减少点数)
occluded_points = torch.randn(1, 80, 7) * 4
occluded_points[:, :, :3] += torch.tensor([2.0, 0, 1.0])

with torch.no_grad():
for name, points in [("室内近距离", indoor_points),
("室外远距离", outdoor_points),
("遮挡场景", occluded_points)]:
pose, root, _ = model(points)
print(f"{name}: 骨盆位置 = [{root[0,0]:.2f}, {root[0,1]:.2f}, {root[0,2]:.2f}], "
f"姿态范围 = [{pose.min():.2f}, {pose.max():.2f}]")


def test_body_token_analysis():
"""人体 Token 空间分析"""
print("\n" + "=" * 60)
print("人体 Token 空间分析")
print("=" * 60)

model = Wave2Body()

# 检查码本
codebook = model.body_tokenizer.codebook.weight
print(f"码本大小: {codebook.shape}")
print(f"码本距离统计: 均值={torch.cdist(codebook, codebook).mean():.4f}, "
f"最大={torch.cdist(codebook, codebook).max():.4f}")

# 生成不同姿态
poses = []
for _ in range(5):
pc = torch.randn(1, 200, 7) * 5
with torch.no_grad():
_, _, tokens = model(pc)
poses.append(tokens)

# 检查 token 多样性
all_tokens = torch.cat(poses, dim=0) # (5, num_tokens)
print(f"\nToken 多样性分析:")
print(f" 唯一 token 数: {len(torch.unique(all_tokens))}")
print(f" Token 分布:")
for t in all_tokens.flatten():
print(f" Token {t.item()}", end="")
print()


if __name__ == "__main__":
model = test_wave2body()
test_cross_domain()
test_body_token_analysis()

5. IMS 开发启示

5.1 OOP 异常姿态检测应用

Wave2Body 的 22 关节骨架输出可直接用于 OOP 分类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class OOPClassifier:
"""基于 Wave2Body 骨架的 OOP 异常姿态分类器"""

def __init__(self):
# SMPL 22 关节索引
self.joints = {
'pelvis': 0, 'spine': 3, 'neck': 12, 'head': 15,
'l_shoulder': 16, 'l_elbow': 18, 'l_wrist': 20,
'r_shoulder': 17, 'r_elbow': 19, 'r_wrist': 21,
'l_hip': 1, 'l_knee': 4, 'l_ankle': 7,
'r_hip': 2, 'r_knee': 5, 'r_ankle': 8
}

def classify(self, pose_3d: np.ndarray) -> dict:
"""
Args:
pose_3d: (22, 3) 3D 关节坐标
Returns:
classification: {posture: str, risk_level: int, metrics: dict}
"""
results = {}

# 1. 躯干前倾角
spine_vec = pose_3d[self.joints['neck']] - pose_3d[self.joints['pelvis']]
forward_angle = np.degrees(np.arctan2(spine_vec[0], spine_vec[2]))
results['forward_lean'] = forward_angle

# 2. 侧倾
shoulder_mid = (pose_3d[self.joints['l_shoulder']] +
pose_3d[self.joints['r_shoulder']]) / 2
hip_mid = (pose_3d[self.joints['l_hip']] +
pose_3d[self.joints['r_hip']]) / 2
side_lean = np.degrees(np.arctan2(
shoulder_mid[0] - hip_mid[0],
shoulder_mid[2] - hip_mid[2]
))
results['side_lean'] = side_lean

# 3. 头部偏转
head_offset = pose_3d[self.joints['head']] - pose_3d[self.joints['neck']]
results['head_offset'] = np.linalg.norm(head_offset)

# 4. 整体高度(判断站立/坐下)
body_height = np.linalg.norm(
pose_3d[self.joints['head']] -
(pose_3d[self.joints['l_ankle']] + pose_3d[self.joints['r_ankle']]) / 2
)
results['body_height'] = body_height

# 分类
if forward_angle > 30:
return {'posture': 'LEANING_FORWARD', 'risk': 2, 'metrics': results}
elif abs(side_lean) > 20:
return {'posture': 'LEANING_SIDE', 'risk': 2, 'metrics': results}
elif body_height > 1.2: # 假设单位为米
return {'posture': 'STANDING', 'risk': 3, 'metrics': results}
else:
return {'posture': 'NORMAL', 'risk': 0, 'metrics': results}

5.2 部署建议

优势 限制
隐私友好(无摄像头) 单人假设(需扩展多人)
跨域泛化强 点云质量依赖传感器
推理极快(3.8ms) 需预训练两个 tokenizer
参数量小(3.2M) 精度低于摄像头方案

5.3 与 RaStream 互补策略

阶段 方案 用途
初始化 Wave2Body 跨域快速骨架估计
精炼 RaStream 时间稳定网格恢复
融合 两者加权 Wave2Body 做骨架,RaStream 做形态

6. 论文局限与未来方向

  1. 单人假设:需扩展到多人场景(座舱多乘员)
  2. 点云依赖:依赖峰值检测,弱反射可能丢失
  3. 绝对位置精度:根定位头精度受雷达观测质量限制
  4. 时间建模缺失:帧级预测,无时间平滑

改进方向: 将 Wave2Body 的解耦设计引入 RaStream 的流式框架——自监督雷达 tokenizer + 组合式 body tokenizer + 流式翻译器。


论文信息