Initial commit
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
//! Attention mechanisms for translation models
|
||||
|
||||
use crate::Result;
|
||||
use rtx_tensor::Tensor;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Attention mechanism types
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum AttentionType {
|
||||
/// Additive (Bahdanau) attention
|
||||
Additive,
|
||||
/// Multiplicative (Luong) attention
|
||||
Multiplicative,
|
||||
/// Scaled dot-product attention
|
||||
ScaledDotProduct,
|
||||
/// Multi-head attention
|
||||
MultiHead { num_heads: usize },
|
||||
}
|
||||
|
||||
/// Attention configuration
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AttentionConfig {
|
||||
pub attention_type: AttentionType,
|
||||
pub hidden_size: usize,
|
||||
pub dropout_rate: f32,
|
||||
pub temperature: f32,
|
||||
}
|
||||
|
||||
impl Default for AttentionConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
attention_type: AttentionType::ScaledDotProduct,
|
||||
hidden_size: 512,
|
||||
dropout_rate: 0.1,
|
||||
temperature: 1.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attention layer for sequence-to-sequence models
|
||||
pub struct AttentionLayer {
|
||||
config: AttentionConfig,
|
||||
}
|
||||
|
||||
impl AttentionLayer {
|
||||
pub fn new(config: AttentionConfig) -> Self {
|
||||
Self { config }
|
||||
}
|
||||
|
||||
/// Apply attention mechanism
|
||||
pub fn forward(
|
||||
&self,
|
||||
query: &Tensor,
|
||||
key: &Tensor,
|
||||
value: &Tensor,
|
||||
mask: Option<&Tensor>,
|
||||
) -> Result<(Tensor, Tensor)> {
|
||||
match &self.config.attention_type {
|
||||
AttentionType::Additive => self.additive_attention(query, key, value, mask),
|
||||
AttentionType::Multiplicative => self.multiplicative_attention(query, key, value, mask),
|
||||
AttentionType::ScaledDotProduct => {
|
||||
self.scaled_dot_product_attention(query, key, value, mask)
|
||||
}
|
||||
AttentionType::MultiHead { num_heads } => {
|
||||
self.multi_head_attention(query, key, value, mask, *num_heads)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn scaled_dot_product_attention(
|
||||
&self,
|
||||
query: &Tensor,
|
||||
key: &Tensor,
|
||||
value: &Tensor,
|
||||
_mask: Option<&Tensor>,
|
||||
) -> Result<(Tensor, Tensor)> {
|
||||
// Compute attention scores: Q * K^T / sqrt(d_k)
|
||||
let key_shape = key.shape().dims();
|
||||
let d_k = key_shape[key_shape.len() - 1] as f32;
|
||||
let scores = (query.matmul(&key.transpose(-2, -1)?)? / d_k.sqrt())?;
|
||||
|
||||
// Apply mask if provided
|
||||
// Note: Stubbed - where_cond not available in rtx-tensor
|
||||
let masked_scores = scores;
|
||||
|
||||
// Apply softmax to get attention weights
|
||||
let attention_weights = masked_scores.softmax(-1)?;
|
||||
|
||||
// Apply attention to values
|
||||
let context = attention_weights.matmul(value)?;
|
||||
|
||||
Ok((context, attention_weights))
|
||||
}
|
||||
|
||||
fn additive_attention(
|
||||
&self,
|
||||
query: &Tensor,
|
||||
key: &Tensor,
|
||||
value: &Tensor,
|
||||
_mask: Option<&Tensor>,
|
||||
) -> Result<(Tensor, Tensor)> {
|
||||
// Mock implementation - in practice would use learned parameters
|
||||
let scores = query.matmul(&key.transpose(-2, -1)?)?;
|
||||
let attention_weights = scores.softmax(-1)?;
|
||||
let context = attention_weights.matmul(value)?;
|
||||
Ok((context, attention_weights))
|
||||
}
|
||||
|
||||
fn multiplicative_attention(
|
||||
&self,
|
||||
query: &Tensor,
|
||||
key: &Tensor,
|
||||
value: &Tensor,
|
||||
_mask: Option<&Tensor>,
|
||||
) -> Result<(Tensor, Tensor)> {
|
||||
// Mock implementation
|
||||
let scores = query.matmul(&key.transpose(-2, -1)?)?;
|
||||
let attention_weights = scores.softmax(-1)?;
|
||||
let context = attention_weights.matmul(value)?;
|
||||
Ok((context, attention_weights))
|
||||
}
|
||||
|
||||
fn multi_head_attention(
|
||||
&self,
|
||||
query: &Tensor,
|
||||
key: &Tensor,
|
||||
value: &Tensor,
|
||||
mask: Option<&Tensor>,
|
||||
_num_heads: usize,
|
||||
) -> Result<(Tensor, Tensor)> {
|
||||
// For simplicity, delegate to scaled dot-product
|
||||
self.scaled_dot_product_attention(query, key, value, mask)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rtx_tensor::Device;
|
||||
|
||||
#[test]
|
||||
fn test_attention_layer() -> Result<()> {
|
||||
let config = AttentionConfig::default();
|
||||
let layer = AttentionLayer::new(config);
|
||||
|
||||
let seq_len = 10;
|
||||
let hidden_size = 64;
|
||||
let device = Device::cuda(0).unwrap_or(Device::default());
|
||||
let query = Tensor::randn(&[1, seq_len, hidden_size], &device)?;
|
||||
let key = Tensor::randn(&[1, seq_len, hidden_size], &device)?;
|
||||
let value = Tensor::randn(&[1, seq_len, hidden_size], &device)?;
|
||||
|
||||
let (context, weights) = layer.forward(&query, &key, &value, None)?;
|
||||
|
||||
assert_eq!(context.shape(), &[1, seq_len, hidden_size]);
|
||||
assert_eq!(weights.shape(), &[1, seq_len, seq_len]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user