peak-end-net / models /rhythm_encoder.py
multimodalart's picture
multimodalart HF Staff
Upload folder using huggingface_hub
26c4860 verified
Raw
History Blame Contribute Delete
4.17 kB
"""
消融实验:美学节奏编码器(移除曲线统计特征)
与原版 experiments_peakaes_v4/aesthetic_rhythm_encoder.py 的区别:
- 移除了 AestheticCurveStatistics 模块(10 维统计特征)
- 只保留 RhythmPatternEncoder(1D CNN 节奏模式特征)
- output_dim: 74 → 64
目的:验证曲线统计特征(均值、标准差、斜率、峰谷差、结尾趋势等)
对最终性能的贡献。
"""
import torch
import torch.nn as nn
import torch.nn.functional as F
class RhythmPatternEncoder(nn.Module):
"""
节奏模式编码器:用 1D CNN 对帧级美学分数序列做卷积,
学习更复杂的时序模式特征。
架构:
[B, 1, T] → Conv1D layers → Global Pool → [B, rhythm_dim]
多尺度卷积核捕捉不同时间跨度的节奏模式:
- 小核(3):捕捉局部波动
- 中核(5):捕捉中程趋势
- 大核(7):捕捉整体节奏
"""
def __init__(self, max_seq_len=32, rhythm_dim=64):
super().__init__()
self.rhythm_dim = rhythm_dim
self.branch_small = nn.Sequential(
nn.Conv1d(1, 16, kernel_size=3, padding=1),
nn.GELU(),
nn.Conv1d(16, 32, kernel_size=3, padding=1),
nn.GELU(),
)
self.branch_medium = nn.Sequential(
nn.Conv1d(1, 16, kernel_size=5, padding=2),
nn.GELU(),
nn.Conv1d(16, 32, kernel_size=5, padding=2),
nn.GELU(),
)
self.branch_large = nn.Sequential(
nn.Conv1d(1, 16, kernel_size=7, padding=3),
nn.GELU(),
nn.Conv1d(16, 32, kernel_size=7, padding=3),
nn.GELU(),
)
self.fusion = nn.Sequential(
nn.Conv1d(32 * 3, 64, kernel_size=1),
nn.GELU(),
)
self.output_proj = nn.Sequential(
nn.Linear(64, rhythm_dim),
nn.GELU(),
)
self.output_norm = nn.LayerNorm(rhythm_dim)
def forward(self, frame_scores, video_mask=None):
"""
Args:
frame_scores: [B, T] 帧级美学分数
video_mask: [B, T] 视频掩码
Returns:
rhythm_features: [B, rhythm_dim] 节奏模式特征
"""
score_sequence = frame_scores.unsqueeze(1) # [B, 1, T]
feat_small = self.branch_small(score_sequence)
feat_medium = self.branch_medium(score_sequence)
feat_large = self.branch_large(score_sequence)
multi_scale = torch.cat([feat_small, feat_medium, feat_large], dim=1) # [B, 96, T]
fused = self.fusion(multi_scale) # [B, 64, T]
if video_mask is not None:
mask_expanded = video_mask.unsqueeze(1).float()
fused = fused * mask_expanded
mask_sum = mask_expanded.sum(dim=-1, keepdim=True).clamp(min=1.0)
pooled = fused.sum(dim=-1) / mask_sum.squeeze(-1)
else:
pooled = fused.mean(dim=-1)
rhythm_features = self.output_proj(pooled)
rhythm_features = self.output_norm(rhythm_features)
return rhythm_features
class AestheticRhythmEncoder(nn.Module):
"""
消融版美学节奏编码器:仅保留 1D CNN 节奏模式特征,移除曲线统计特征。
原版 output_dim = 10 (统计) + 64 (CNN) = 74
消融版 output_dim = 64 (仅 CNN)
"""
def __init__(self, end_ratio=0.25, max_seq_len=32, rhythm_dim=64):
super().__init__()
self.rhythm_pattern_encoder = RhythmPatternEncoder(
max_seq_len=max_seq_len, rhythm_dim=rhythm_dim
)
self.output_dim = rhythm_dim # 64,不再包含统计特征的 10 维
def forward(self, frame_scores, video_mask=None):
"""
Args:
frame_scores: [B, T] 帧级美学分数
video_mask: [B, T] 视频掩码
Returns:
rhythm_features: [B, output_dim] 节奏特征(仅 CNN)
curve_stats: None(消融掉了,保留接口兼容性)
"""
rhythm_features = self.rhythm_pattern_encoder(frame_scores, video_mask)
return rhythm_features, None