```
feat: 添加模型定义、优化器和训练器功能 - 实现Model接口和Sequential序列模型,支持模型定义和前向传播 - 添加SGD和Adam优化器,支持参数更新和梯度清零 - 创建Trainer训练器,提供完整的训练和评估流程 - 实现模型保存和加载功能,支持参数序列化 - 更新README文档,添加模型训练示例和API文档 - 重构README中的功能特性和示例代码 ```
This commit is contained in:
parent
3e9e913dd4
commit
fcc2e54144
170
README.md
170
README.md
|
|
@ -1,16 +1,19 @@
|
||||||
# gotensor
|
# gotensor
|
||||||
|
|
||||||
gotensor 是一个用 Go 语言编写的张量计算库,提供了基本的张量运算、自动微分和反向传播功能。该项目旨在为 Go 语言开发者提供一个高效、易用的张量计算工具。
|
gotensor 是一个用 Go 语言实现的张量计算库,专注于为 Go 开发者提供高效的数值计算能力,支持自动微分和反向传播,适用于构建轻量级机器学习模型。
|
||||||
|
|
||||||
## 功能特性
|
## 功能特性
|
||||||
|
|
||||||
- 基本张量运算:加法、减法、乘法、矩阵乘法等
|
- 基本张量运算:加法、减法、乘法、矩阵乘法
|
||||||
- 张量操作:数乘、转置等
|
- 数乘、转置等张量操作
|
||||||
- 自动微分和反向传播
|
- 自动微分与反向传播机制
|
||||||
- 激活函数:Sigmoid、ReLU、Softmax等
|
- 激活函数:Sigmoid、ReLU、Softmax
|
||||||
- 卷积和池化操作:Conv2D、MaxPool2D、AvgPool2D等
|
- 卷积与池化操作:Conv2D、MaxPool2D、AvgPool2D
|
||||||
- 神经网络层:Flatten、损失函数等
|
- 神经网络层与损失函数:Flatten、CrossEntropy、MSE
|
||||||
- 支持多种初始化方式:零张量、单位矩阵、随机张量等
|
- 多种初始化方式:零张量、单位矩阵、随机张量
|
||||||
|
- 模型定义和训练支持
|
||||||
|
- 模型保存和加载
|
||||||
|
- 多种优化器:SGD、Adam
|
||||||
|
|
||||||
## 安装
|
## 安装
|
||||||
|
|
||||||
|
|
@ -29,106 +32,105 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// 创建两个2x2的张量
|
// 创建张量
|
||||||
t1_data := []float64{1, 2, 3, 4}
|
tensor1, _ := gotensor.NewTensor([]float64{1, 2, 3}, []int{1, 3})
|
||||||
t1_shape := []int{2, 2}
|
tensor2, _ := gotensor.NewTensor([]float64{4, 5, 6}, []int{3, 1})
|
||||||
t1, err := gotensor.NewTensor(t1_data, t1_shape)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t2_data := []float64{5, 6, 7, 8}
|
// 执行矩阵乘法
|
||||||
t2, err := gotensor.NewTensor(t2_data, t1_shape)
|
result, _ := tensor1.MatMul(tensor2)
|
||||||
if err != nil {
|
fmt.Println(result)
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 执行加法运算
|
|
||||||
result, err := t1.Add(t2)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("结果:\n%s\n", result.String())
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 示例
|
## 模型训练示例
|
||||||
|
|
||||||
项目包含多个示例,展示如何使用 gotensor:
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
- [基本运算示例](examples/basic_operations.go):展示基本的张量运算
|
import (
|
||||||
- [自动微分示例](examples/autograd_example.go):演示自动微分和反向传播
|
"fmt"
|
||||||
- [线性回归示例](examples/linear_regression.go):使用 gotensor 实现简单的线性回归
|
"git.kingecg.top/kingecg/gotensor"
|
||||||
- [CNN示例](examples/cnn_example.go):使用卷积、池化等操作构建简单的卷积神经网络
|
)
|
||||||
|
|
||||||
运行示例:
|
func main() {
|
||||||
|
// 创建模型(例如:简单的线性回归模型)
|
||||||
|
// 这里可以使用Sequential模型或者自定义模型
|
||||||
|
|
||||||
```bash
|
// 定义一些示例数据
|
||||||
# 基本运算示例
|
input, _ := gotensor.NewTensor([]float64{1, 2, 3, 4}, []int{2, 2})
|
||||||
go run examples/basic_operations.go
|
target, _ := gotensor.NewTensor([]float64{5, 6, 7, 8}, []int{2, 2})
|
||||||
|
|
||||||
# 自动微分示例
|
// 创建模型参数
|
||||||
go run examples/autograd_example.go
|
weights, _ := gotensor.NewTensor([]float64{0.5, 0.3, 0.2, 0.4}, []int{2, 2})
|
||||||
|
|
||||||
# 线性回归示例
|
// 定义模型(这里简化为单个张量,实际中会是更复杂的结构)
|
||||||
go run examples/linear_regression.go
|
// ...
|
||||||
|
|
||||||
# CNN示例
|
// 定义优化器
|
||||||
go run examples/cnn_example.go
|
optimizer := gotensor.NewSGD([]*gotensor.Tensor{weights}, 0.01)
|
||||||
|
|
||||||
|
// 创建训练器
|
||||||
|
trainer := gotensor.NewTrainer(nil, optimizer) // 需要传入实际模型
|
||||||
|
|
||||||
|
// 开始训练
|
||||||
|
// trainer.Train(trainInputs, trainTargets, epochs, lossFn, true)
|
||||||
|
|
||||||
|
fmt.Println("Training example")
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## API 文档
|
## API 文档
|
||||||
|
|
||||||
### 创建张量
|
### 张量操作
|
||||||
|
|
||||||
- `NewTensor(data []float64, shape []int)` - 创建新的张量
|
- `NewTensor(data []float64, shape []int)`: 创建新张量
|
||||||
- `NewZeros(shape []int)` - 创建零张量
|
- `Add(other *Tensor)`: 张量加法
|
||||||
- `NewOnes(shape []int)` - 创建全一张量
|
- `Subtract(other *Tensor)`: 张量减法
|
||||||
- `NewIdentity(size int)` - 创建单位矩阵
|
- `Multiply(other *Tensor)`: 张量逐元素乘法
|
||||||
|
- `MatMul(other *Tensor)`: 矩阵乘法
|
||||||
### 张量运算
|
- `Scale(factor float64)`: 张量数乘
|
||||||
|
- `Sigmoid()`: Sigmoid激活函数
|
||||||
- `Add(other *Tensor)` - 张量加法
|
- `ReLU()`: ReLU激活函数
|
||||||
- `Subtract(other *Tensor)` - 张量减法
|
- `Softmax()`: Softmax函数
|
||||||
- `Multiply(other *Tensor)` - 张量逐元素乘法
|
- `Backward()`: 反向传播
|
||||||
- `MatMul(other *Tensor)` - 矩阵乘法
|
|
||||||
- `Scale(factor float64)` - 数乘
|
|
||||||
|
|
||||||
### 激活函数
|
|
||||||
|
|
||||||
- `Sigmoid()` - Sigmoid激活函数
|
|
||||||
- `ReLU()` - ReLU激活函数
|
|
||||||
- `Softmax()` - Softmax激活函数
|
|
||||||
|
|
||||||
### 卷积和池化
|
|
||||||
|
|
||||||
- `Conv2D(kernel *Tensor, stride, padding int)` - 二维卷积操作
|
|
||||||
- `MaxPool2D(kernelSize, stride int)` - 二维最大池化
|
|
||||||
- `AvgPool2D(kernelSize, stride int)` - 二维平均池化
|
|
||||||
|
|
||||||
### 神经网络层
|
### 神经网络层
|
||||||
|
|
||||||
- `Flatten()` - 将多维张量展平为一维
|
- `Flatten()`: 展平张量
|
||||||
- `CrossEntropy(target *Tensor)` - 交叉熵损失函数
|
- `CrossEntropy(target *Tensor)`: 交叉熵损失
|
||||||
- `MeanSquaredError(target *Tensor)` - 均方误差损失函数
|
- `MeanSquaredError(target *Tensor)`: 均方误差损失
|
||||||
|
|
||||||
### 其他方法
|
### 模型定义
|
||||||
|
|
||||||
- `ZeroGrad()` - 将梯度置零
|
- `Sequential`: 序列模型
|
||||||
- `Shape()` - 返回张量形状
|
- `Model` 接口: 模型的基本接口
|
||||||
- `Size()` - 返回张量大小
|
- `SaveModel(model Model, filepath string)`: 保存模型
|
||||||
- `Get(indices ...int)` - 获取指定位置的值
|
- `LoadModel(model Model, filepath string)`: 加载模型
|
||||||
- `Set(value float64, indices ...int)` - 设置指定位置的值
|
|
||||||
- `Backward()` - 执行反向传播
|
|
||||||
|
|
||||||
## 测试
|
### 优化器
|
||||||
|
|
||||||
运行测试:
|
- `SGD`: 随机梯度下降
|
||||||
|
- `Adam`: Adam优化算法
|
||||||
|
|
||||||
```bash
|
### 训练器
|
||||||
go test
|
|
||||||
```
|
- `Trainer`: 训练管理器
|
||||||
|
- `NewTrainer(model Model, optimizer Optimizer)`: 创建训练器
|
||||||
|
- `Train(...)`: 执行训练
|
||||||
|
- `Evaluate(...)`: 评估模型
|
||||||
|
|
||||||
|
## 示例
|
||||||
|
|
||||||
|
项目包含多个示例:
|
||||||
|
|
||||||
|
- `examples/basic_operation`: 基本张量运算示例
|
||||||
|
- `examples/autograd`: 自动微分示例
|
||||||
|
- `examples/linear_regression`: 线性回归示例
|
||||||
|
- `examples/cnn_example.go`: 卷积神经网络示例
|
||||||
|
|
||||||
|
## 贡献
|
||||||
|
|
||||||
|
欢迎提交 Issue 和 Pull Request 来帮助改进 gotensor!
|
||||||
|
|
||||||
## 许可证
|
## 许可证
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,124 @@
|
||||||
|
package gotensor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Model 模型接口定义
|
||||||
|
type Model interface {
|
||||||
|
Forward(inputs *Tensor) (*Tensor, error)
|
||||||
|
Parameters() []*Tensor // 获取模型所有参数
|
||||||
|
ZeroGrad() // 将所有参数的梯度清零
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sequential 序列模型,按顺序执行层
|
||||||
|
type Sequential struct {
|
||||||
|
Layers []Layer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Layer 接口定义
|
||||||
|
type Layer interface {
|
||||||
|
Forward(inputs *Tensor) (*Tensor, error)
|
||||||
|
Parameters() []*Tensor
|
||||||
|
ZeroGrad()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward 实现前向传播
|
||||||
|
func (s *Sequential) Forward(inputs *Tensor) (*Tensor, error) {
|
||||||
|
output := inputs
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for _, layer := range s.Layers {
|
||||||
|
output, err = layer.Forward(output)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parameters 获取模型所有参数
|
||||||
|
func (s *Sequential) Parameters() []*Tensor {
|
||||||
|
var params []*Tensor
|
||||||
|
for _, layer := range s.Layers {
|
||||||
|
params = append(params, layer.Parameters()...)
|
||||||
|
}
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZeroGrad 将所有参数梯度清零
|
||||||
|
func (s *Sequential) ZeroGrad() {
|
||||||
|
for _, layer := range s.Layers {
|
||||||
|
layer.ZeroGrad()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveModel 保存模型参数到文件
|
||||||
|
func SaveModel(model Model, filepath string) error {
|
||||||
|
params := model.Parameters()
|
||||||
|
paramsData := make([][]float64, len(params))
|
||||||
|
|
||||||
|
for i, param := range params {
|
||||||
|
shape := param.Shape()
|
||||||
|
size := param.Size()
|
||||||
|
data := make([]float64, size)
|
||||||
|
|
||||||
|
for idx := 0; idx < size; idx++ {
|
||||||
|
if len(shape) == 1 {
|
||||||
|
data[idx], _ = param.Data.Get(idx)
|
||||||
|
} else if len(shape) == 2 {
|
||||||
|
cols := shape[1]
|
||||||
|
data[idx], _ = param.Data.Get(idx/cols, idx%cols)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
paramsData[i] = data
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Create(filepath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
return json.NewEncoder(file).Encode(paramsData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadModel 从文件加载模型参数
|
||||||
|
func LoadModel(model Model, filepath string) error {
|
||||||
|
file, err := os.Open(filepath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
var paramsData [][]float64
|
||||||
|
err = json.NewDecoder(file).Decode(¶msData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
params := model.Parameters()
|
||||||
|
if len(params) != len(paramsData) {
|
||||||
|
return nil // 参数数量不匹配,返回错误
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, param := range params {
|
||||||
|
data := paramsData[i]
|
||||||
|
shape := param.Shape()
|
||||||
|
|
||||||
|
if len(shape) == 1 {
|
||||||
|
for idx, val := range data {
|
||||||
|
param.Data.Set(val, idx)
|
||||||
|
}
|
||||||
|
} else if len(shape) == 2 {
|
||||||
|
cols := shape[1]
|
||||||
|
for idx, val := range data {
|
||||||
|
param.Data.Set(val, idx/cols, idx%cols)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,213 @@
|
||||||
|
package gotensor
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
// Optimizer 优化器接口
|
||||||
|
type Optimizer interface {
|
||||||
|
Step() // 根据梯度更新参数
|
||||||
|
ZeroGrad() // 清空所有梯度
|
||||||
|
}
|
||||||
|
|
||||||
|
// SGD 随机梯度下降优化器
|
||||||
|
type SGD struct {
|
||||||
|
Parameters []*Tensor
|
||||||
|
LR float64 // 学习率
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSGD 创建一个新的SGD优化器
|
||||||
|
func NewSGD(parameters []*Tensor, lr float64) *SGD {
|
||||||
|
return &SGD{
|
||||||
|
Parameters: parameters,
|
||||||
|
LR: lr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 更新参数
|
||||||
|
func (s *SGD) Step() {
|
||||||
|
for _, param := range s.Parameters {
|
||||||
|
// 获取参数的梯度
|
||||||
|
grad := param.Grad
|
||||||
|
|
||||||
|
// 获取参数的形状
|
||||||
|
shape := param.Data.Shape()
|
||||||
|
|
||||||
|
// 更新参数: param = param - lr * grad
|
||||||
|
if len(shape) == 1 {
|
||||||
|
for i := 0; i < shape[0]; i++ {
|
||||||
|
paramVal, _ := param.Data.Get(i)
|
||||||
|
gradVal, _ := grad.Get(i)
|
||||||
|
newVal := paramVal - s.LR * gradVal
|
||||||
|
param.Data.Set(newVal, i)
|
||||||
|
}
|
||||||
|
} else if len(shape) == 2 {
|
||||||
|
rows, cols := shape[0], shape[1]
|
||||||
|
for i := 0; i < rows; i++ {
|
||||||
|
for j := 0; j < cols; j++ {
|
||||||
|
paramVal, _ := param.Data.Get(i, j)
|
||||||
|
gradVal, _ := grad.Get(i, j)
|
||||||
|
newVal := paramVal - s.LR * gradVal
|
||||||
|
param.Data.Set(newVal, i, j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZeroGrad 清空所有梯度
|
||||||
|
func (s *SGD) ZeroGrad() {
|
||||||
|
for _, param := range s.Parameters {
|
||||||
|
param.ZeroGrad()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adam 优化器
|
||||||
|
type Adam struct {
|
||||||
|
Parameters []*Tensor
|
||||||
|
LR float64 // 学习率
|
||||||
|
Beta1 float64 // 一阶矩估计的指数衰减率
|
||||||
|
Beta2 float64 // 二阶矩估计的指数衰减率
|
||||||
|
Epsilon float64 // 防止除零的小常数
|
||||||
|
T int // 当前步数
|
||||||
|
|
||||||
|
// 一阶矩估计
|
||||||
|
M []map[string]*Tensor
|
||||||
|
// 二阶矩估计
|
||||||
|
V []map[string]*Tensor
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAdam 创建一个新的Adam优化器
|
||||||
|
func NewAdam(parameters []*Tensor, lr, beta1, beta2, epsilon float64) *Adam {
|
||||||
|
adam := &Adam{
|
||||||
|
Parameters: parameters,
|
||||||
|
LR: lr,
|
||||||
|
Beta1: beta1,
|
||||||
|
Beta2: beta2,
|
||||||
|
Epsilon: epsilon,
|
||||||
|
T: 0,
|
||||||
|
M: make([]map[string]*Tensor, len(parameters)),
|
||||||
|
V: make([]map[string]*Tensor, len(parameters)),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化M和V
|
||||||
|
for i := range parameters {
|
||||||
|
adam.M[i] = make(map[string]*Tensor)
|
||||||
|
adam.V[i] = make(map[string]*Tensor)
|
||||||
|
|
||||||
|
// 创建与参数形状相同的零张量
|
||||||
|
shape := parameters[i].Shape()
|
||||||
|
m, _ := NewZeros(shape)
|
||||||
|
v, _ := NewZeros(shape)
|
||||||
|
|
||||||
|
adam.M[i]["tensor"] = m
|
||||||
|
adam.V[i]["tensor"] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
return adam
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 更新参数
|
||||||
|
func (a *Adam) Step() {
|
||||||
|
a.T++
|
||||||
|
|
||||||
|
for i, param := range a.Parameters {
|
||||||
|
grad := param.Grad
|
||||||
|
shape := param.Data.Shape()
|
||||||
|
|
||||||
|
// 更新一阶矩估计: m = beta1 * m + (1 - beta1) * grad
|
||||||
|
m := a.M[i]["tensor"]
|
||||||
|
newMData := make([]float64, param.Size())
|
||||||
|
|
||||||
|
if len(shape) == 1 {
|
||||||
|
for idx := 0; idx < shape[0]; idx++ {
|
||||||
|
mVal, _ := m.Data.Get(idx)
|
||||||
|
gradVal, _ := grad.Get(idx)
|
||||||
|
newMData[idx] = a.Beta1 * mVal + (1 - a.Beta1) * gradVal
|
||||||
|
}
|
||||||
|
} else if len(shape) == 2 {
|
||||||
|
rows, cols := shape[0], shape[1]
|
||||||
|
for r := 0; r < rows; r++ {
|
||||||
|
for c := 0; c < cols; c++ {
|
||||||
|
mVal, _ := m.Data.Get(r, c)
|
||||||
|
gradVal, _ := grad.Get(r, c)
|
||||||
|
newMData[r*cols+c] = a.Beta1 * mVal + (1 - a.Beta1) * gradVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newM, _ := NewTensor(newMData, shape)
|
||||||
|
a.M[i]["tensor"] = newM
|
||||||
|
|
||||||
|
// 更新二阶矩估计: v = beta2 * v + (1 - beta2) * grad^2
|
||||||
|
v := a.V[i]["tensor"]
|
||||||
|
newVData := make([]float64, param.Size())
|
||||||
|
|
||||||
|
if len(shape) == 1 {
|
||||||
|
for idx := 0; idx < shape[0]; idx++ {
|
||||||
|
vVal, _ := v.Data.Get(idx)
|
||||||
|
gradVal, _ := grad.Get(idx)
|
||||||
|
newVData[idx] = a.Beta2 * vVal + (1 - a.Beta2) * gradVal * gradVal
|
||||||
|
}
|
||||||
|
} else if len(shape) == 2 {
|
||||||
|
rows, cols := shape[0], shape[1]
|
||||||
|
for r := 0; r < rows; r++ {
|
||||||
|
for c := 0; c < cols; c++ {
|
||||||
|
vVal, _ := v.Data.Get(r, c)
|
||||||
|
gradVal, _ := grad.Get(r, c)
|
||||||
|
newVData[r*cols+c] = a.Beta2 * vVal + (1 - a.Beta2) * gradVal * gradVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newV, _ := NewTensor(newVData, shape)
|
||||||
|
a.V[i]["tensor"] = newV
|
||||||
|
|
||||||
|
// 计算偏差修正的一阶矩估计
|
||||||
|
mHatData := make([]float64, param.Size())
|
||||||
|
mHatShape := shape
|
||||||
|
for idx := 0; idx < param.Size(); idx++ {
|
||||||
|
mVal, _ := newM.Data.Get(idx)
|
||||||
|
mHatData[idx] = mVal / (1 - math.Pow(a.Beta1, float64(a.T)))
|
||||||
|
}
|
||||||
|
mHat, _ := NewTensor(mHatData, mHatShape)
|
||||||
|
|
||||||
|
// 计算偏差修正的二阶矩估计
|
||||||
|
vHatData := make([]float64, param.Size())
|
||||||
|
vHatShape := shape
|
||||||
|
for idx := 0; idx < param.Size(); idx++ {
|
||||||
|
vVal, _ := newV.Data.Get(idx)
|
||||||
|
vHatData[idx] = vVal / (1 - math.Pow(a.Beta2, float64(a.T)))
|
||||||
|
}
|
||||||
|
vHat, _ := NewTensor(vHatData, vHatShape)
|
||||||
|
|
||||||
|
// 更新参数: param = param - lr * m_hat / (sqrt(v_hat) + epsilon)
|
||||||
|
if len(shape) == 1 {
|
||||||
|
for idx := 0; idx < shape[0]; idx++ {
|
||||||
|
paramVal, _ := param.Data.Get(idx)
|
||||||
|
mHatVal, _ := mHat.Data.Get(idx)
|
||||||
|
vHatVal, _ := vHat.Data.Get(idx)
|
||||||
|
updateVal := a.LR * mHatVal / (math.Sqrt(vHatVal) + a.Epsilon)
|
||||||
|
newVal := paramVal - updateVal
|
||||||
|
param.Data.Set(newVal, idx)
|
||||||
|
}
|
||||||
|
} else if len(shape) == 2 {
|
||||||
|
rows, cols := shape[0], shape[1]
|
||||||
|
for r := 0; r < rows; r++ {
|
||||||
|
for c := 0; c < cols; c++ {
|
||||||
|
paramVal, _ := param.Data.Get(r, c)
|
||||||
|
mHatVal, _ := mHat.Data.Get(r, c)
|
||||||
|
vHatVal, _ := vHat.Data.Get(r, c)
|
||||||
|
updateVal := a.LR * mHatVal / (math.Sqrt(vHatVal) + a.Epsilon)
|
||||||
|
newVal := paramVal - updateVal
|
||||||
|
param.Data.Set(newVal, r, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZeroGrad 清空所有梯度
|
||||||
|
func (a *Adam) ZeroGrad() {
|
||||||
|
for _, param := range a.Parameters {
|
||||||
|
param.ZeroGrad()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
package gotensor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Trainer 训练器结构,管理整个训练过程
|
||||||
|
type Trainer struct {
|
||||||
|
Model Model
|
||||||
|
Optimizer Optimizer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTrainer 创建新的训练器
|
||||||
|
func NewTrainer(model Model, optimizer Optimizer) *Trainer {
|
||||||
|
return &Trainer{
|
||||||
|
Model: model,
|
||||||
|
Optimizer: optimizer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrainEpoch 训练一个epoch
|
||||||
|
func (t *Trainer) TrainEpoch(inputs []*Tensor, targets []*Tensor, lossFn func(*Tensor, *Tensor) *Tensor) (float64, error) {
|
||||||
|
var totalLoss float64
|
||||||
|
|
||||||
|
for i := 0; i < len(inputs); i++ {
|
||||||
|
// 前向传播
|
||||||
|
output, err := t.Model.Forward(inputs[i])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算损失
|
||||||
|
loss := lossFn(output, targets[i])
|
||||||
|
lossVal, _ := loss.Data.Get(0)
|
||||||
|
totalLoss += lossVal
|
||||||
|
|
||||||
|
// 反向传播
|
||||||
|
loss.Backward()
|
||||||
|
|
||||||
|
// 更新参数
|
||||||
|
t.Optimizer.Step()
|
||||||
|
|
||||||
|
// 清空梯度
|
||||||
|
t.Optimizer.ZeroGrad()
|
||||||
|
}
|
||||||
|
|
||||||
|
avgLoss := totalLoss / float64(len(inputs))
|
||||||
|
return avgLoss, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Train 完整的训练过程
|
||||||
|
func (t *Trainer) Train(
|
||||||
|
trainInputs []*Tensor,
|
||||||
|
trainTargets []*Tensor,
|
||||||
|
epochs int,
|
||||||
|
lossFn func(*Tensor, *Tensor) *Tensor,
|
||||||
|
verbose bool,
|
||||||
|
) error {
|
||||||
|
for epoch := 0; epoch < epochs; epoch++ {
|
||||||
|
avgLoss, err := t.TrainEpoch(trainInputs, trainTargets, lossFn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if verbose {
|
||||||
|
fmt.Printf("Epoch [%d/%d], Loss: %.6f\n", epoch+1, epochs, avgLoss)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate 评估模型性能
|
||||||
|
func (t *Trainer) Evaluate(testInputs []*Tensor, testTargets []*Tensor, lossFn func(*Tensor, *Tensor) *Tensor) (float64, error) {
|
||||||
|
var totalLoss float64
|
||||||
|
|
||||||
|
for i := 0; i < len(testInputs); i++ {
|
||||||
|
// 前向传播
|
||||||
|
output, err := t.Model.Forward(testInputs[i])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算损失
|
||||||
|
loss := lossFn(output, testTargets[i])
|
||||||
|
lossVal, _ := loss.Data.Get(0)
|
||||||
|
totalLoss += lossVal
|
||||||
|
}
|
||||||
|
|
||||||
|
avgLoss := totalLoss / float64(len(testInputs))
|
||||||
|
return avgLoss, nil
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue