481 lines
11 KiB
Go
481 lines
11 KiB
Go
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
|
||
} |