gotensor/trainer_test.go

321 lines
6.9 KiB
Go

package gotensor
import (
"testing"
"git.kingecg.top/kingecg/gomatrix"
)
// MockLayer 是一个用于测试的模拟层
type MockLayer struct {
Weight *Tensor
}
// NewMockLayer 创建一个新的模拟层
func NewMockLayer() *MockLayer {
weight, _ := gomatrix.NewMatrix([]float64{0.5, 0.3, 0.4, 0.7}, []int{2, 2})
grad, _ := gomatrix.NewZeros([]int{2, 2})
return &MockLayer{
Weight: &Tensor{
Data: weight,
Grad: grad,
},
}
}
func (m *MockLayer) Forward(inputs *Tensor) (*Tensor, error) {
// 简单的矩阵乘法
output, err := m.Weight.MatMul(inputs)
if err != nil {
return nil, err
}
return output, nil
}
func (m *MockLayer) Parameters() []*Tensor {
return []*Tensor{m.Weight}
}
func (m *MockLayer) ZeroGrad() {
m.Weight.ZeroGrad()
}
// MockModel 是一个用于测试的模拟模型
type MockModel struct {
Layer *MockLayer
}
func (m *MockModel) Forward(inputs *Tensor) (*Tensor, error) {
return m.Layer.Forward(inputs)
}
func (m *MockModel) Parameters() []*Tensor {
return m.Layer.Parameters()
}
func (m *MockModel) ZeroGrad() {
m.Layer.ZeroGrad()
}
func NewVector(data []float64) (*Tensor, error) {
return NewTensor(data, []int{len(data), 1})
}
func Must[T any](t *T, err error) *T {
if err != nil {
panic(err)
}
return t
}
// TestTrainer 测试训练器的基本功能
func TestTrainer(t *testing.T) {
// 创建模型
mockLayer := NewMockLayer()
model := &MockModel{
Layer: mockLayer,
}
// 创建优化器
optimizer := NewSGD(model.Parameters(), 0.01)
// 创建训练器
trainer := NewTrainer(model, optimizer)
// 创建训练数据
inputs := []*Tensor{
Must(NewVector([]float64{1, 0})),
Must(NewVector([]float64{0, 1})),
}
targets := []*Tensor{
Must(NewVector([]float64{1, 0})),
Must(NewVector([]float64{0, 1})),
}
// 定义损失函数
lossFn := func(output, target *Tensor) *Tensor {
// MSE 损失函数
diff, _ := output.Data.Subtract(target.Data)
squared, _ := diff.Multiply(diff)
// 计算矩阵元素的总和
var total float64
shape := squared.Shape()
if len(shape) == 1 {
for i := 0; i < squared.Size(); i++ {
val, _ := squared.Get(i)
total += val
}
} else if len(shape) == 2 {
rows, cols := shape[0], shape[1]
for i := 0; i < rows; i++ {
for j := 0; j < cols; j++ {
val, _ := squared.Get(i, j)
total += val
}
}
} else {
// 对于其他维度,遍历所有元素
for i := 0; i < squared.Size(); i++ {
var val float64
var err error
if len(shape) == 1 {
val, err = squared.Get(i)
} else if len(shape) == 2 {
cols := shape[1]
val, err = squared.Get(i/cols, i%cols)
} else {
// 对于高维张量,按顺序访问元素
val, err = squared.Get(i)
}
if err != nil {
continue
}
total += val
}
}
size := float64(output.Size())
resultVal := total / size
// 创建结果张量
result, _ := NewTensor([]float64{resultVal}, []int{1})
return result
}
// 测试TrainEpoch
avgLoss, err := trainer.TrainEpoch(inputs, targets, lossFn)
if err != nil {
t.Errorf("TrainEpoch failed: %v", err)
}
if avgLoss < 0 {
t.Errorf("Expected non-negative loss, got %v", avgLoss)
}
// 测试Evaluate
evalLoss, err := trainer.Evaluate(inputs, targets, lossFn)
if err != nil {
t.Errorf("Evaluate failed: %v", err)
}
if evalLoss < 0 {
t.Errorf("Expected non-negative evaluation loss, got %v", evalLoss)
}
}
// TestTrainerFullTrain 测试完整的训练过程
func TestTrainerFullTrain(t *testing.T) {
// 创建一个简单的线性回归模型
mockLayer := NewMockLayer()
model := &MockModel{
Layer: mockLayer,
}
// 创建优化器
optimizer := NewSGD(model.Parameters(), 0.1)
// 创建训练器
trainer := NewTrainer(model, optimizer)
// 创建训练数据 (简单的 y = x 示例)
trainInputs := []*Tensor{
Must(NewVector([]float64{1, 0})),
Must(NewVector([]float64{0, 1})),
Must(NewVector([]float64{1, 1})),
}
trainTargets := []*Tensor{
Must(NewVector([]float64{1, 0})),
Must(NewVector([]float64{0, 1})),
Must(NewVector([]float64{1, 1})),
}
// 定义损失函数
lossFn := func(output, target *Tensor) *Tensor {
// MSE 损失函数
diff, _ := output.Data.Subtract(target.Data)
squared, _ := diff.Multiply(diff)
sum := squared.Sum()
size := float64(output.Size())
result := sum / size
return Must(NewTensor([]float64{result}, []int{1}))
// return result
}
// 训练模型
epochs := 5
err := trainer.Train(trainInputs, trainTargets, epochs, lossFn, false)
if err != nil {
t.Errorf("Full training failed: %v", err)
}
// 验证训练后的损失应该比训练前低(理想情况下)
evalLoss, err := trainer.Evaluate(trainInputs, trainTargets, lossFn)
if err != nil {
t.Errorf("Post-training evaluation failed: %v", err)
}
if evalLoss < 0 {
t.Errorf("Expected non-negative evaluation loss after training, got %v", evalLoss)
}
}
// TestTrainerWithDifferentOptimizers 测试使用不同优化器的训练器
func TestTrainerWithDifferentOptimizers(t *testing.T) {
// 创建模型
mockLayer := NewMockLayer()
model := &MockModel{
Layer: mockLayer,
}
// 测试Adam优化器
adamOpt := NewAdam(model.Parameters(), 0.001, 0.9, 0.999, 1e-8)
trainer := NewTrainer(model, adamOpt)
// 创建训练数据
inputs := []*Tensor{
Must(NewVector([]float64{1, 0})),
Must(NewVector([]float64{0, 1})),
}
targets := []*Tensor{
Must(NewVector([]float64{1, 0})),
Must(NewVector([]float64{0, 1})),
}
// 损失函数
lossFn := func(output, target *Tensor) *Tensor {
diff, _ := output.Data.Subtract(target.Data)
squared, _ := diff.Multiply(diff)
// 计算矩阵元素的总和
var total float64
shape := squared.Shape()
if len(shape) == 1 {
for i := 0; i < squared.Size(); i++ {
val, _ := squared.Get(i)
total += val
}
} else if len(shape) == 2 {
rows, cols := shape[0], shape[1]
for i := 0; i < rows; i++ {
for j := 0; j < cols; j++ {
val, _ := squared.Get(i, j)
total += val
}
}
} else {
// 对于其他维度,遍历所有元素
for i := 0; i < squared.Size(); i++ {
var val float64
var err error
if len(shape) == 1 {
val, err = squared.Get(i)
} else if len(shape) == 2 {
cols := shape[1]
val, err = squared.Get(i/cols, i%cols)
} else {
// 对于高维张量,按顺序访问元素
val, err = squared.Get(i)
}
if err != nil {
continue
}
total += val
}
}
size := float64(output.Size())
resultVal := total / size
// 创建结果张量
result, _ := NewTensor([]float64{resultVal}, []int{1})
return result
}
// 训练
err := trainer.Train(inputs, targets, 3, lossFn, false)
if err != nil {
t.Errorf("Training with Adam optimizer failed: %v", err)
}
evalLoss, err := trainer.Evaluate(inputs, targets, lossFn)
if err != nil {
t.Errorf("Evaluation with Adam optimizer failed: %v", err)
}
if evalLoss < 0 {
t.Errorf("Expected non-negative evaluation loss with Adam, got %v", evalLoss)
}
}