gotensor/layers.go

481 lines
11 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package gotensor
import (
"git.kingecg.top/kingecg/gomatrix"
"math"
)
// Flatten 将多维张量展平为一维
func (t *Tensor) Flatten() *Tensor {
totalSize := t.Size()
flatShape := []int{totalSize}
// 创建新数据切片
flatData := make([]float64, totalSize)
// 将原数据复制到扁平化数组
shape := t.Data.Shape()
if len(shape) == 2 {
rows, cols := shape[0], shape[1]
for i := 0; i < rows; i++ {
for j := 0; j < cols; j++ {
val, _ := t.Data.Get(i, j)
flatData[i*cols+j] = val
}
}
} else if len(shape) == 4 {
// 假设为 [batch, channel, height, width] 格式
b, c, h, w := shape[0], shape[1], shape[2], shape[3]
for bi := 0; bi < b; bi++ {
for ci := 0; ci < c; ci++ {
for hi := 0; hi < h; hi++ {
for wi := 0; wi < w; wi++ {
srcIdx := ((bi*c+ci)*h+hi)*w + wi
val, _ := t.Data.Get(bi, ci, hi, wi)
flatData[srcIdx] = val
}
}
}
}
} else {
// 对于其他情况,直接复制数据
for i := 0; i < totalSize; i++ {
val, _ := t.Data.Get(i)
flatData[i] = val
}
}
resultMatrix, _ := gomatrix.NewMatrix(flatData, flatShape)
output := &Tensor{
Data: resultMatrix,
Op: "flatten",
}
output.Prevs[0] = t
output.Num_Prevs = 1
output.backwardFunc = func() {
if t.Grad != nil {
// 将梯度重塑回原始形状
originalShape := t.Data.Shape()
originalSize := t.Size()
// 创建一个临时数组来保存重塑后的梯度
reshapedGradData := make([]float64, originalSize)
// 根据原始形状复制梯度数据
if len(originalShape) == 2 {
rows, cols := originalShape[0], originalShape[1]
for i := 0; i < rows; i++ {
for j := 0; j < cols; j++ {
srcIdx := i*cols + j
gradVal, _ := output.Grad.Get(srcIdx)
reshapedGradData[srcIdx] = gradVal
}
}
} else if len(originalShape) == 4 {
b, c, h, w := originalShape[0], originalShape[1], originalShape[2], originalShape[3]
for bi := 0; bi < b; bi++ {
for ci := 0; ci < c; ci++ {
for hi := 0; hi < h; hi++ {
for wi := 0; wi < w; wi++ {
srcIdx := ((bi*c+ci)*h+hi)*w + wi
gradVal, _ := output.Grad.Get(srcIdx)
reshapedGradData[srcIdx] = gradVal
}
}
}
}
} else {
for i := 0; i < originalSize; i++ {
gradVal, _ := output.Grad.Get(i)
reshapedGradData[i] = gradVal
}
}
// 创建重塑后的梯度矩阵
reshapedGradMatrix, _ := gomatrix.NewMatrix(reshapedGradData, originalShape)
// 将重塑后的梯度加到原张量的梯度上
newGrad, _ := t.Grad.Add(reshapedGradMatrix)
t.Grad = newGrad
}
}
return output
}
// CrossEntropy 交叉熵损失函数
func (t *Tensor) CrossEntropy(target *Tensor) *Tensor {
// 计算预测值的概率分布
predictions := t.Softmax()
// 计算交叉熵损失
// loss = -sum(target * log(predictions + epsilon))
epsilon := 1e-15 // 防止log(0)
predData := predictions.Data
targetData := target.Data
// 计算log(predictions + epsilon)
logPredData := make([]float64, predData.Size())
for i := 0; i < predData.Size(); i++ {
var val float64
var err error
shape := predData.Shape()
if len(shape) == 1 {
val, err = predData.Get(i)
} else if len(shape) == 2 {
_, cols := shape[0], shape[1]
val, err = predData.Get(i/cols, i%cols)
} else {
// 对于其他情况,简单地按索引获取
val, err = predData.Get(i)
}
if err != nil {
continue
}
logPredData[i] = math.Log(val + epsilon)
}
logPredMatrix, _ := gomatrix.NewMatrix(logPredData, predData.Shape())
// 计算target * log(predictions + epsilon)
multiplied, _ := targetData.Multiply(logPredMatrix)
// 求和得到损失值
var totalLoss float64
for i := 0; i < multiplied.Size(); i++ {
var val float64
var err error
shape := multiplied.Shape()
if len(shape) == 1 {
val, err = multiplied.Get(i)
} else if len(shape) == 2 {
_, cols := shape[0], shape[1]
val, err = multiplied.Get(i/cols, i%cols)
} else {
// 对于其他情况,简单地按索引获取
val, err = multiplied.Get(i)
}
if err != nil {
continue
}
totalLoss += val
}
totalLoss = -totalLoss
// 创建损失张量(标量)
lossValue := []float64{totalLoss}
lossMatrix, _ := gomatrix.NewMatrix(lossValue, []int{1})
output := &Tensor{
Data: lossMatrix,
Op: "cross_entropy",
}
output.Prevs[0] = t
output.Num_Prevs = 1
output.backwardFunc = func() {
if t.Grad != nil {
// 计算交叉熵损失相对于预测值的梯度
predProbs := predictions.Data
targetVals := target.Data
gradData := make([]float64, predProbs.Size())
for i := 0; i < predProbs.Size(); i++ {
var predVal, targetVal float64
var err error
predShape := predProbs.Shape()
if len(predShape) == 1 {
predVal, err = predProbs.Get(i)
if err != nil {
continue
}
targetVal, err = targetVals.Get(i)
if err != nil {
continue
}
} else if len(predShape) == 2 {
_, cols := predShape[0], predShape[1]
predVal, err = predProbs.Get(i/cols, i%cols)
if err != nil {
continue
}
targetVal, err = targetVals.Get(i/cols, i%cols)
if err != nil {
continue
}
} else {
predVal, err = predProbs.Get(i)
if err != nil {
continue
}
targetVal, err = targetVals.Get(i)
if err != nil {
continue
}
}
// 交叉熵损失的梯度是 pred - target
gradData[i] = (predVal - targetVal) / float64(predProbs.Size())
}
gradMatrix, _ := gomatrix.NewMatrix(gradData, predProbs.Shape())
// 将梯度加到原张量的梯度上
newGrad, _ := t.Grad.Add(gradMatrix)
t.Grad = newGrad
}
}
return output
}
// Softmax 激活函数
func (t *Tensor) Softmax() *Tensor {
// 假设输入是二维的,第二维是类别维度
shape := t.Data.Shape()
if len(shape) != 2 {
// 如果不是二维,则将其视为一维(单个样本)
origSize := t.Size()
shape = []int{1, origSize}
}
rows, cols := shape[0], shape[1]
// 计算softmax按行进行
resultData := make([]float64, t.Size())
for r := 0; r < rows; r++ {
// 找到当前行的最大值,用于数值稳定性
maxVal := -math.Inf(1)
for c := 0; c < cols; c++ {
var val float64
var err error
if len(t.Data.Shape()) == 2 {
val, err = t.Data.Get(r, c)
} else {
// 如果原数据不是二维的,我们按顺序取值
val, err = t.Data.Get(r*cols+c)
}
if err != nil {
continue
}
if val > maxVal {
maxVal = val
}
}
// 计算指数和
expSum := 0.0
tempVals := make([]float64, cols)
for c := 0; c < cols; c++ {
var val float64
var err error
if len(t.Data.Shape()) == 2 {
val, err = t.Data.Get(r, c)
} else {
// 如果原数据不是二维的,我们按顺序取值
val, err = t.Data.Get(r*cols+c)
}
if err != nil {
continue
}
expVal := math.Exp(val - maxVal) // 减去最大值提高数值稳定性
tempVals[c] = expVal
expSum += expVal
}
// 计算softmax值
for c := 0; c < cols; c++ {
resultData[r*cols+c] = tempVals[c] / expSum
}
}
resultMatrix, _ := gomatrix.NewMatrix(resultData, shape)
output := &Tensor{
Data: resultMatrix,
Op: "softmax",
}
output.Prevs[0] = t
output.Num_Prevs = 1
output.backwardFunc = func() {
if t.Grad != nil {
// 计算softmax的梯度
gradData := make([]float64, t.Size())
for r := 0; r < rows; r++ {
for c1 := 0; c1 < cols; c1++ {
gradSum := 0.0
for c2 := 0; c2 < cols; c2++ {
var s1, s2 float64
var err error
if len(resultMatrix.Shape()) == 2 {
s1, err = resultMatrix.Get(r, c1)
if err != nil {
continue
}
s2, err = resultMatrix.Get(r, c2)
if err != nil {
continue
}
} else {
s1, err = resultMatrix.Get(r*cols+c1)
if err != nil {
continue
}
s2, err = resultMatrix.Get(r*cols+c2)
if err != nil {
continue
}
}
var ds_din float64
if c1 == c2 {
ds_din = s1 * (1 - s2) // softmax导数si*(1-sj) if i=j
} else {
ds_din = -s1 * s2 // softmax导数-si*sj if i!=j
}
var outGrad float64
if len(output.Grad.Shape()) == 2 {
outGrad, _ = output.Grad.Get(r, c2)
} else {
outGrad, _ = output.Grad.Get(r*cols+c2)
}
gradSum += ds_din * outGrad
}
gradData[r*cols+c1] = gradSum
}
}
gradMatrix, _ := gomatrix.NewMatrix(gradData, shape)
// 将梯度加到原张量的梯度上
newGrad, _ := t.Grad.Add(gradMatrix)
t.Grad = newGrad
}
}
return output
}
// MeanSquaredError 均方误差损失函数
func (t *Tensor) MeanSquaredError(target *Tensor) *Tensor {
// 计算预测值和真实值之间的差值
diff, _ := t.Data.Subtract(target.Data)
// 计算平方差
squaredDiff, _ := diff.Multiply(diff)
// 计算均值
var total float64
for i := 0; i < squaredDiff.Size(); i++ {
var val float64
var err error
shape := squaredDiff.Shape()
if len(shape) == 1 {
val, err = squaredDiff.Get(i)
} else if len(shape) == 2 {
_, cols := shape[0], shape[1]
val, err = squaredDiff.Get(i/cols, i%cols)
} else {
// 对于其他情况,简单地按索引获取
val, err = squaredDiff.Get(i)
}
if err != nil {
continue
}
total += val
}
mseVal := total / float64(squaredDiff.Size())
mseData := []float64{mseVal}
mseMatrix, _ := gomatrix.NewMatrix(mseData, []int{1})
output := &Tensor{
Data: mseMatrix,
Op: "mse",
}
output.Prevs[0] = t
output.Num_Prevs = 1
output.backwardFunc = func() {
if t.Grad != nil {
// MSE损失函数的梯度是 2*(prediction - target)/N
gradData := make([]float64, t.Size())
n := float64(t.Size())
for i := 0; i < t.Size(); i++ {
var predVal, targetVal float64
var err error
shape := t.Data.Shape()
if len(shape) == 1 {
predVal, err = t.Data.Get(i)
if err != nil {
continue
}
targetVal, err = target.Data.Get(i)
if err != nil {
continue
}
} else if len(shape) == 2 {
_, cols := shape[0], shape[1]
predVal, err = t.Data.Get(i/cols, i%cols)
if err != nil {
continue
}
targetVal, err = target.Data.Get(i/cols, i%cols)
if err != nil {
continue
}
} else {
// 对于其他情况
predVal, err = t.Data.Get(i)
if err != nil {
continue
}
targetVal, err = target.Data.Get(i)
if err != nil {
continue
}
}
gradData[i] = 2 * (predVal - targetVal) / n
}
gradMatrix, _ := gomatrix.NewMatrix(gradData, t.Data.Shape())
// 将梯度加到原张量的梯度上
newGrad, _ := t.Grad.Add(gradMatrix)
t.Grad = newGrad
}
}
return output
}