Files
rustytorch/crates/models/rtx-nlg/src/translation/transformer.rs
T
2026-03-04 00:08:42 +00:00

313 lines
9.3 KiB
Rust

//! Transformer-based translation implementation
use crate::{Result, translation::AttentionConfig, translation::AttentionLayer};
use rtx_tensor::{Device, Tensor};
use serde::{Deserialize, Serialize};
/// Transformer configuration for translation
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TransformerConfig {
pub num_encoder_layers: usize,
pub num_decoder_layers: usize,
pub hidden_size: usize,
pub num_attention_heads: usize,
pub intermediate_size: usize,
pub dropout_rate: f32,
pub max_position_embeddings: usize,
pub vocab_size: usize,
}
impl Default for TransformerConfig {
fn default() -> Self {
Self {
num_encoder_layers: 6,
num_decoder_layers: 6,
hidden_size: 512,
num_attention_heads: 8,
intermediate_size: 2048,
dropout_rate: 0.1,
max_position_embeddings: 512,
vocab_size: 50000,
}
}
}
/// Transformer encoder layer
pub struct TransformerEncoderLayer {
self_attention: AttentionLayer,
feed_forward: FeedForward,
config: TransformerConfig,
}
impl TransformerEncoderLayer {
pub fn new(config: TransformerConfig) -> Self {
let attention_config = AttentionConfig {
attention_type: crate::translation::attention::AttentionType::MultiHead {
num_heads: config.num_attention_heads,
},
hidden_size: config.hidden_size,
dropout_rate: config.dropout_rate,
temperature: 1.0,
};
Self {
self_attention: AttentionLayer::new(attention_config),
feed_forward: FeedForward::new(config.hidden_size, config.intermediate_size),
config,
}
}
pub fn forward(
&self,
hidden_states: &Tensor,
attention_mask: Option<&Tensor>,
) -> Result<Tensor> {
// Self-attention
let (attention_output, _) = self.self_attention.forward(
hidden_states,
hidden_states,
hidden_states,
attention_mask,
)?;
// Residual connection and layer norm
let normed_attention = self.layer_norm(&(hidden_states + &attention_output)?)?;
// Feed-forward
let ff_output = self.feed_forward.forward(&normed_attention)?;
// Residual connection and layer norm
let output = self.layer_norm(&(&normed_attention + &ff_output)?)?;
Ok(output)
}
fn layer_norm(&self, input: &Tensor) -> Result<Tensor> {
// Mock layer normalization
Ok(input.clone())
}
}
/// Transformer decoder layer
pub struct TransformerDecoderLayer {
self_attention: AttentionLayer,
cross_attention: AttentionLayer,
feed_forward: FeedForward,
config: TransformerConfig,
}
impl TransformerDecoderLayer {
pub fn new(config: TransformerConfig) -> Self {
let attention_config = AttentionConfig {
attention_type: crate::translation::attention::AttentionType::MultiHead {
num_heads: config.num_attention_heads,
},
hidden_size: config.hidden_size,
dropout_rate: config.dropout_rate,
temperature: 1.0,
};
Self {
self_attention: AttentionLayer::new(attention_config.clone()),
cross_attention: AttentionLayer::new(attention_config),
feed_forward: FeedForward::new(config.hidden_size, config.intermediate_size),
config,
}
}
pub fn forward(
&self,
hidden_states: &Tensor,
encoder_hidden_states: &Tensor,
attention_mask: Option<&Tensor>,
causal_mask: Option<&Tensor>,
) -> Result<(Tensor, Tensor)> {
// Self-attention with causal mask
let (self_attn_output, _) = self.self_attention.forward(
hidden_states,
hidden_states,
hidden_states,
causal_mask,
)?;
let normed_self_attn = self.layer_norm(&(hidden_states + &self_attn_output)?)?;
// Cross-attention
let (cross_attn_output, cross_attn_weights) = self.cross_attention.forward(
&normed_self_attn,
encoder_hidden_states,
encoder_hidden_states,
attention_mask,
)?;
let normed_cross_attn = self.layer_norm(&(&normed_self_attn + &cross_attn_output)?)?;
// Feed-forward
let ff_output = self.feed_forward.forward(&normed_cross_attn)?;
let output = self.layer_norm(&(&normed_cross_attn + &ff_output)?)?;
Ok((output, cross_attn_weights))
}
fn layer_norm(&self, input: &Tensor) -> Result<Tensor> {
// Mock layer normalization
Ok(input.clone())
}
}
/// Feed-forward network
pub struct FeedForward {
hidden_size: usize,
intermediate_size: usize,
}
impl FeedForward {
pub fn new(hidden_size: usize, intermediate_size: usize) -> Self {
Self {
hidden_size,
intermediate_size,
}
}
pub fn forward(&self, input: &Tensor) -> Result<Tensor> {
// Mock feed-forward implementation
// Would typically be: linear -> activation -> linear
Ok(input.clone())
}
}
/// Complete transformer model for translation
pub struct TransformerTranslator {
encoder_layers: Vec<TransformerEncoderLayer>,
decoder_layers: Vec<TransformerDecoderLayer>,
config: TransformerConfig,
}
impl TransformerTranslator {
pub fn new(config: TransformerConfig) -> Self {
let encoder_layers: Vec<_> = (0..config.num_encoder_layers)
.map(|_| TransformerEncoderLayer::new(config.clone()))
.collect();
let decoder_layers: Vec<_> = (0..config.num_decoder_layers)
.map(|_| TransformerDecoderLayer::new(config.clone()))
.collect();
Self {
encoder_layers,
decoder_layers,
config,
}
}
pub fn encode(&self, input_ids: &Tensor, attention_mask: Option<&Tensor>) -> Result<Tensor> {
let mut hidden_states = self.embed_tokens(input_ids)?;
for layer in &self.encoder_layers {
hidden_states = layer.forward(&hidden_states, attention_mask)?;
}
Ok(hidden_states)
}
pub fn decode(
&self,
target_ids: &Tensor,
encoder_hidden_states: &Tensor,
attention_mask: Option<&Tensor>,
) -> Result<(Tensor, Vec<Tensor>)> {
let mut hidden_states = self.embed_tokens(target_ids)?;
let causal_mask = self.create_causal_mask(target_ids.shape()[1])?;
let mut cross_attention_weights = Vec::new();
for layer in &self.decoder_layers {
let (new_hidden_states, cross_attn_weights) = layer.forward(
&hidden_states,
encoder_hidden_states,
attention_mask,
Some(&causal_mask),
)?;
hidden_states = new_hidden_states;
cross_attention_weights.push(cross_attn_weights);
}
Ok((hidden_states, cross_attention_weights))
}
fn embed_tokens(&self, input_ids: &Tensor) -> Result<Tensor> {
// Mock token embedding + positional encoding
let batch_size = input_ids.shape()[0];
let seq_length = input_ids.shape()[1];
let embeddings = Tensor::randn(
&[batch_size, seq_length, self.config.hidden_size],
input_ids.device(),
)?;
Ok(embeddings)
}
fn create_causal_mask(&self, seq_length: usize) -> Result<Tensor> {
// Create lower triangular mask for causal attention
let mut mask_data = vec![0.0f32; seq_length * seq_length];
for i in 0..seq_length {
for j in 0..=i {
mask_data[i * seq_length + j] = 1.0;
}
}
let mask = Tensor::from_data(mask_data, [seq_length, seq_length], &Device::default())?;
Ok(mask)
}
}
#[cfg(test)]
mod tests {
use super::*;
use rtx_tensor::Device;
#[test]
fn test_transformer_config() {
let config = TransformerConfig::default();
assert_eq!(config.num_encoder_layers, 6);
assert_eq!(config.num_decoder_layers, 6);
assert_eq!(config.hidden_size, 512);
}
#[test]
fn test_feed_forward() -> Result<()> {
let ff = FeedForward::new(512, 2048);
let device = Device::cuda(0).unwrap_or(Device::default());
let input = Tensor::randn(&[1, 10, 512], &device)?;
let output = ff.forward(&input)?;
assert_eq!(output.shape(), input.shape());
Ok(())
}
#[test]
fn test_transformer_translator() -> Result<()> {
let config = TransformerConfig {
hidden_size: 64,
num_attention_heads: 4,
..Default::default()
};
let translator = TransformerTranslator::new(config);
let input_ids = Tensor::from_data(
vec![1.0f32, 2.0, 3.0, 4.0, 5.0],
&[1, 5],
&Device::default(),
)?;
let encoder_output = translator.encode(&input_ids, None)?;
assert_eq!(encoder_output.shape(), &[1, 5, 64]);
let target_ids = Tensor::from_data(vec![1.0f32, 2.0, 3.0], &[1, 3], &Device::default())?;
let (decoder_output, attention_weights) =
translator.decode(&target_ids, &encoder_output, None)?;
assert_eq!(decoder_output.shape(), &[1, 3, 64]);
assert!(!attention_weights.is_empty());
Ok(())
}
}