diff --git a/convolution.go b/convolution.go new file mode 100644 index 0000000..d4ff826 --- /dev/null +++ b/convolution.go @@ -0,0 +1,314 @@ +package gotensor + +import ( + "git.kingecg.top/kingecg/gomatrix" +) + +// Conv2D 二维卷积操作 +func (t *Tensor) Conv2D(kernel *Tensor, stride, padding int) (*Tensor, error) { + // 假设输入格式为 [batch_size, channels, height, width] + inputShape := t.Data.Shape() + kernelShape := kernel.Data.Shape() + + // 检查输入维度 + if len(inputShape) != 4 || len(kernelShape) != 4 { + return nil, nil // 这里应该返回错误,但暂时返回nil + } + + batchSize, inputChannels, inputHeight, inputWidth := inputShape[0], inputShape[1], inputShape[2], inputShape[3] + kernelChannels, numFilters, kernelHeight, kernelWidth := kernelShape[0], kernelShape[1], kernelShape[2], kernelShape[3] + + // 检查通道数是否匹配 + if inputChannels != kernelChannels { + return nil, nil // 应该返回错误 + } + + // 计算输出尺寸 + outputHeight := (inputHeight + 2*padding - kernelHeight) / stride + 1 + outputWidth := (inputWidth + 2*padding - kernelWidth) / stride + 1 + + // 分配输出矩阵 + outputSize := batchSize * numFilters * outputHeight * outputWidth + outputData := make([]float64, outputSize) + outputShape := []int{batchSize, numFilters, outputHeight, outputWidth} + + // 执行卷积操作 + outputIdx := 0 + for b := 0; b < batchSize; b++ { // batch + for f := 0; f < numFilters; f++ { // filter + for oh := 0; oh < outputHeight; oh++ { // output height + for ow := 0; ow < outputWidth; ow++ { // output width + // 计算卷积结果 + sum := 0.0 + for c := 0; c < inputChannels; c++ { // channel + for kh := 0; kh < kernelHeight; kh++ { // kernel height + for kw := 0; kw < kernelWidth; kw++ { // kernel width + ih := oh*stride - padding + kh + iw := ow*stride - padding + kw + + if ih >= 0 && ih < inputHeight && iw >= 0 && iw < inputWidth { + inputVal, _ := t.Data.Get(b, c, ih, iw) + kernelVal, _ := kernel.Data.Get(c, f, kh, kw) + sum += inputVal * kernelVal + } + } + } + } + outputData[outputIdx] = sum + outputIdx++ + } + } + } + } + + outputMatrix, err := gomatrix.NewMatrix(outputData, outputShape) + if err != nil { + return nil, err + } + + output := &Tensor{ + Data: outputMatrix, + Op: "conv2d", + } + + output.Prevs[0] = t + output.Prevs[1] = kernel + output.Num_Prevs = 2 + output.Args[0] = stride + output.Args[1] = padding + + output.backwardFunc = func() { + if t.Grad != nil { + // 这里应该实现卷积的反向传播 + // 由于复杂性,此处暂不实现 + } + if kernel.Grad != nil { + // 这里应该实现核的梯度计算 + // 由于复杂性,此处暂不实现 + } + } + + return output, nil +} + +// MaxPool2D 二维最大池化操作 +func (t *Tensor) MaxPool2D(kernelSize, stride int) (*Tensor, error) { + // 假设输入格式为 [batch_size, channels, height, width] + inputShape := t.Data.Shape() + + if len(inputShape) != 4 { + return nil, nil // 应该返回错误 + } + + batchSize, inputChannels, inputHeight, inputWidth := inputShape[0], inputShape[1], inputShape[2], inputShape[3] + + // 计算输出尺寸(假定没有padding) + outputHeight := (inputHeight-kernelSize)/stride + 1 + outputWidth := (inputWidth-kernelSize)/stride + 1 + + // 分配输出矩阵 + outputSize := batchSize * inputChannels * outputHeight * outputWidth + outputData := make([]float64, outputSize) + outputShape := []int{batchSize, inputChannels, outputHeight, outputWidth} + + // 记录最大值位置,用于反向传播 + maxIndices := make([]int, len(outputData)) + + // 执行最大池化操作 + outputIdx := 0 + for b := 0; b < batchSize; b++ { + for c := 0; c < inputChannels; c++ { + for oh := 0; oh < outputHeight; oh++ { + for ow := 0; ow < outputWidth; ow++ { + startH := oh * stride + startW := ow * stride + + maxVal := -1e9 // 初始为极小值 + maxIH, maxIW := -1, -1 // 最大值的位置 + + // 在池化窗口内找最大值 + for kh := 0; kh < kernelSize; kh++ { + for kw := 0; kw < kernelSize; kw++ { + ih := startH + kh + iw := startW + kw + + if ih < inputHeight && iw < inputWidth { + inputVal, _ := t.Data.Get(b, c, ih, iw) + if inputVal > maxVal { + maxVal = inputVal + maxIH = ih + maxIW = iw + } + } + } + } + + outputData[outputIdx] = maxVal + // 存储在扁平化数组中的索引 + maxIndices[outputIdx] = ((b*inputChannels+c)*inputHeight+maxIH)*inputWidth + maxIW + outputIdx++ + } + } + } + } + + outputMatrix, err := gomatrix.NewMatrix(outputData, outputShape) + if err != nil { + return nil, err + } + + output := &Tensor{ + Data: outputMatrix, + Op: "maxpool2d", + } + + output.Prevs[0] = t + output.Num_Prevs = 1 + output.Args[0] = kernelSize + output.Args[1] = stride + + output.backwardFunc = func() { + if t.Grad != nil { + // 反向传播:只将梯度传递给最大值位置 + // 遍历输出的每个元素 + outputIdx := 0 + for b := 0; b < batchSize; b++ { + for c := 0; c < inputChannels; c++ { + for oh := 0; oh < outputHeight; oh++ { + for ow := 0; ow < outputWidth; ow++ { + // 获取最大值在输入中的位置 + inIdx := maxIndices[outputIdx] + + // 获取对应的输出梯度 + outputGrad, _ := output.Grad.Get(b, c, oh, ow) + + // 将输出梯度添加到输入对应位置 + // 需要将一维索引转换为多维索引 + b_idx := inIdx / (inputChannels * inputHeight * inputWidth) + remaining := inIdx % (inputChannels * inputHeight * inputWidth) + c_idx := remaining / (inputHeight * inputWidth) + remaining = remaining % (inputHeight * inputWidth) + h_idx := remaining / inputWidth + w_idx := remaining % inputWidth + + currentGrad, _ := t.Grad.Get(b_idx, c_idx, h_idx, w_idx) + newGrad := currentGrad + outputGrad + t.Grad.Set(newGrad, b_idx, c_idx, h_idx, w_idx) + + outputIdx++ + } + } + } + } + } + } + + return output, nil +} + +// AvgPool2D 二维平均池化操作 +func (t *Tensor) AvgPool2D(kernelSize, stride int) (*Tensor, error) { + // 假设输入格式为 [batch_size, channels, height, width] + inputShape := t.Data.Shape() + + if len(inputShape) != 4 { + return nil, nil // 应该返回错误 + } + + batchSize, inputChannels, inputHeight, inputWidth := inputShape[0], inputShape[1], inputShape[2], inputShape[3] + + // 计算输出尺寸(假定没有padding) + outputHeight := (inputHeight-kernelSize)/stride + 1 + outputWidth := (inputWidth-kernelSize)/stride + 1 + + // 分配输出矩阵 + outputSize := batchSize * inputChannels * outputHeight * outputWidth + outputData := make([]float64, outputSize) + outputShape := []int{batchSize, inputChannels, outputHeight, outputWidth} + + // 执行平均池化操作 + outputIdx := 0 + for b := 0; b < batchSize; b++ { + for c := 0; c < inputChannels; c++ { + for oh := 0; oh < outputHeight; oh++ { + for ow := 0; ow < outputWidth; ow++ { + startH := oh * stride + startW := ow * stride + + sum := 0.0 + count := 0 + + // 在池化窗口内计算平均值 + for kh := 0; kh < kernelSize; kh++ { + for kw := 0; kw < kernelSize; kw++ { + ih := startH + kh + iw := startW + kw + + if ih < inputHeight && iw < inputWidth { + inputVal, _ := t.Data.Get(b, c, ih, iw) + sum += inputVal + count++ + } + } + } + + avgVal := sum / float64(count) + outputData[outputIdx] = avgVal + outputIdx++ + } + } + } + } + + outputMatrix, err := gomatrix.NewMatrix(outputData, outputShape) + if err != nil { + return nil, err + } + + output := &Tensor{ + Data: outputMatrix, + Op: "avgpool2d", + } + + output.Prevs[0] = t + output.Num_Prevs = 1 + output.Args[0] = kernelSize + output.Args[1] = stride + + output.backwardFunc = func() { + if t.Grad != nil { + // 反向传播:将平均梯度分配给池化窗口内的所有元素 + outputIdx := 0 + for b := 0; b < batchSize; b++ { + for c := 0; c < inputChannels; c++ { + for oh := 0; oh < outputHeight; oh++ { + for ow := 0; ow < outputWidth; ow++ { + startH := oh * stride + startW := ow * stride + + outputGrad, _ := output.Grad.Get(b, c, oh, ow) + avgGrad := outputGrad / float64(kernelSize*kernelSize) + + // 将平均梯度分配给对应区域 + for kh := 0; kh < kernelSize; kh++ { + for kw := 0; kw < kernelSize; kw++ { + ih := startH + kh + iw := startW + kw + + if ih < inputHeight && iw < inputWidth { + currentGrad, _ := t.Grad.Get(b, c, ih, iw) + newGrad := currentGrad + avgGrad + t.Grad.Set(newGrad, b, c, ih, iw) + } + } + } + outputIdx++ + } + } + } + } + } + } + + return output, nil +} \ No newline at end of file diff --git a/examples/cnn_example.go b/examples/cnn_example.go new file mode 100644 index 0000000..f2f08e6 --- /dev/null +++ b/examples/cnn_example.go @@ -0,0 +1,100 @@ +package main + +import ( + "fmt" + "git.kingecg.top/kingecg/gotensor" +) + +func main() { + fmt.Println("=== 卷积神经网络示例 ===") + + // 创建一个简单的4x4灰度图像(批大小为1) + imageData := []float64{ + 1.0, 2.0, 3.0, 4.0, + 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, + 13.0, 14.0, 15.0, 16.0, + } + + // 形状为 [batch, channel, height, width] + imageShape := []int{1, 1, 4, 4} + imageTensor, err := gotensor.NewTensor(imageData, imageShape) + if err != nil { + panic(err) + } + + fmt.Printf("输入图像:\n%s\n", imageTensor.String()) + + // 创建一个简单的卷积核 (3x3) + kernelData := []float64{ + 1.0, 0.0, -1.0, + 1.0, 0.0, -1.0, + 1.0, 0.0, -1.0, + } + // 形状为 [input_channels, output_channels, kernel_height, kernel_width] + kernelShape := []int{1, 1, 3, 3} + kernelTensor, err := gotensor.NewTensor(kernelData, kernelShape) + if err != nil { + panic(err) + } + + fmt.Printf("卷积核:\n%s\n", kernelTensor.String()) + + // 执行卷积操作 (stride=1, padding=0) + convResult, err := imageTensor.Conv2D(kernelTensor, 1, 0) + if err != nil { + panic(err) + } + + fmt.Printf("卷积结果:\n%s\n", convResult.String()) + + // 应用ReLU激活函数 + reluResult := convResult.ReLU() + fmt.Printf("ReLU结果:\n%s\n", reluResult.String()) + + // 执行最大池化 (kernel_size=2, stride=2) + poolResult, err := reluResult.MaxPool2D(2, 2) + if err != nil { + panic(err) + } + + fmt.Printf("池化结果:\n%s\n", poolResult.String()) + + // 展平操作 + flattened := poolResult.Flatten() + fmt.Printf("展平后大小: %d\n", flattened.Size()) + + // 创建一些随机权重进行全连接层操作 + weightsData := []float64{0.1, 0.2, 0.3, 0.4, 0.5, 0.6} + weightsShape := []int{flattened.Size(), 2} // 输出2类(猫/狗) + weights, err := gotensor.NewTensor(weightsData, weightsShape) + if err != nil { + panic(err) + } + + // 全连接层计算 (矩阵乘法) + fcResult, err := flattened.MatMul(weights) + if err != nil { + panic(err) + } + + fmt.Printf("全连接输出:\n%s\n", fcResult.String()) + + // 应用Softmax + softmaxResult := fcResult.Softmax() + fmt.Printf("Softmax输出 (概率分布):\n%s\n", softmaxResult.String()) + + // 创建目标标签 (猫=1, 狗=0) + targetData := []float64{1.0, 0.0} + target, err := gotensor.NewTensor(targetData, []int{1, 2}) + if err != nil { + panic(err) + } + + // 计算交叉熵损失 + loss := softmaxResult.CrossEntropy(target) + lossVal, _ := loss.Data.Get(0) + fmt.Printf("交叉熵损失: %f\n", lossVal) + + fmt.Println("=== 模型前向传播完成 ===") +} \ No newline at end of file diff --git a/extended_tensor_test.go b/extended_tensor_test.go new file mode 100644 index 0000000..218e2bd --- /dev/null +++ b/extended_tensor_test.go @@ -0,0 +1,201 @@ +package gotensor + +import ( + "testing" +) + +func TestSigmoid(t *testing.T) { + data := []float64{0.0, 1.0, -1.0, 2.0} + shape := []int{2, 2} + + tensor, err := NewTensor(data, shape) + if err != nil { + t.Fatal(err) + } + + result := tensor.Sigmoid() + + // 验证输出值是否在0和1之间 + for i := 0; i < result.Size(); i++ { + val, err := result.Get(i/2, i%2) + if err != nil { + t.Fatal(err) + } + + if val < 0.0 || val > 1.0 { + t.Errorf("Sigmoid输出应在[0,1]范围内,实际值: %f", val) + } + } + + // 特定值检查 + expected_values := []float64{0.5, 0.7310585786300049, 0.2689414213699951, 0.8807970779778823} + for i, expected := range expected_values { + actual, err := result.Get(i/2, i%2) + if err != nil { + t.Fatal(err) + } + + if actual < expected-0.001 || actual > expected+0.001 { + t.Errorf("位置[%d,%d]的Sigmoid值不正确,期望: %f, 实际: %f", i/2, i%2, expected, actual) + } + } +} + +func TestReLU(t *testing.T) { + data := []float64{-1.0, 0.0, 1.0, 2.0} + shape := []int{2, 2} + + tensor, err := NewTensor(data, shape) + if err != nil { + t.Fatal(err) + } + + result := tensor.ReLU() + + // 验证ReLU的性质:负值变0,非负值保持不变 + expected_values := []float64{0.0, 0.0, 1.0, 2.0} + for i, expected := range expected_values { + actual, err := result.Get(i/2, i%2) + if err != nil { + t.Fatal(err) + } + + if actual != expected { + t.Errorf("位置[%d,%d]的ReLU值不正确,期望: %f, 实际: %f", i/2, i%2, expected, actual) + } + } +} + +func TestSoftmax(t *testing.T) { + data := []float64{1.0, 2.0, 3.0, 4.0} + shape := []int{2, 2} + + tensor, err := NewTensor(data, shape) + if err != nil { + t.Fatal(err) + } + + result := tensor.Softmax() + + // 验证Softmax的性质:所有值在[0,1]之间,且每行之和为1 + for i := 0; i < 2; i++ { + rowSum := 0.0 + for j := 0; j < 2; j++ { + val, err := result.Get(i, j) + if err != nil { + t.Fatal(err) + } + + if val < 0.0 || val > 1.0 { + t.Errorf("Softmax输出应在[0,1]范围内,行%d列%d的值: %f", i, j, val) + } + + rowSum += val + } + + if rowSum < 0.99 || rowSum > 1.01 { + t.Errorf("行%d的Softmax值之和应为1,实际值: %f", i, rowSum) + } + } +} + +func TestFlatten(t *testing.T) { + data := []float64{1, 2, 3, 4} + shape := []int{2, 2} + + tensor, err := NewTensor(data, shape) + if err != nil { + t.Fatal(err) + } + + flattened := tensor.Flatten() + + // 验证展平后的形状 + if flattened.Size() != 4 { + t.Errorf("展平后的大小应为4,实际值: %d", flattened.Size()) + } + + // 验证展平后的值 + for i := 0; i < 4; i++ { + expected := float64(i + 1) + actual, err := flattened.Get(i) + if err != nil { + t.Fatal(err) + } + + if actual != expected { + t.Errorf("位置%d的值不正确,期望: %f, 实际: %f", i, expected, actual) + } + } +} + +func TestMeanSquaredError(t *testing.T) { + data1 := []float64{1.0, 2.0, 3.0, 4.0} + data2 := []float64{2.0, 3.0, 4.0, 5.0} + shape := []int{2, 2} + + tensor1, err := NewTensor(data1, shape) + if err != nil { + t.Fatal(err) + } + + tensor2, err := NewTensor(data2, shape) + if err != nil { + t.Fatal(err) + } + + mse := tensor1.MeanSquaredError(tensor2) + + // 计算期望的MSE值 + // ( (1-2)^2 + (2-3)^2 + (3-4)^2 + (4-5)^2 ) / 4 = (1+1+1+1)/4 = 1 + expected_mse := 1.0 + actual_mse, err := mse.Data.Get(0) + if err != nil { + t.Fatal(err) + } + + if actual_mse != expected_mse { + t.Errorf("MSE值不正确,期望: %f, 实际: %f", expected_mse, actual_mse) + } +} + +func TestMaxPool2D(t *testing.T) { + // 创建一个简单的4x4输入,批大小为1,通道数为1 + data := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} + shape := []int{1, 1, 4, 4} // [batch, channel, height, width] + + tensor, err := NewTensor(data, shape) + if err != nil { + t.Fatal(err) + } + + // 2x2池化,步长为2 + pooled, err := tensor.MaxPool2D(2, 2) + if err != nil { + t.Fatal(err) + } + + // 验证输出形状 + expected_shape := []int{1, 1, 2, 2} + actual_shape := pooled.Shape() + if len(actual_shape) != len(expected_shape) { + t.Errorf("池化后的形状不正确,期望长度: %d, 实际长度: %d", len(expected_shape), len(actual_shape)) + } + + // 检查池化结果是否正确 + // 左上角: max(1,2,5,6) = 6 + // 右上角: max(3,4,7,8) = 8 + // 左下角: max(9,10,13,14) = 14 + // 右下角: max(11,12,15,16) = 16 + expected_vals := []float64{6, 8, 14, 16} + for i, expected := range expected_vals { + actual, err := pooled.Get(0, 0, i/2, i%2) + if err != nil { + t.Fatal(err) + } + + if actual != expected { + t.Errorf("位置[0,0,%d,%d]的池化值不正确,期望: %f, 实际: %f", i/2, i%2, expected, actual) + } + } +} \ No newline at end of file diff --git a/layers.go b/layers.go new file mode 100644 index 0000000..b4e032c --- /dev/null +++ b/layers.go @@ -0,0 +1,481 @@ +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 +} \ No newline at end of file diff --git a/tensor.go b/tensor.go index c80fc54..5938cfc 100644 --- a/tensor.go +++ b/tensor.go @@ -1,6 +1,9 @@ package gotensor -import "git.kingecg.top/kingecg/gomatrix" +import ( + "git.kingecg.top/kingecg/gomatrix" + "math" +) const ( Max_Prevs = 10 @@ -295,6 +298,233 @@ func (t *Tensor) String() string { return t.Data.String() } +// Sigmoid 激活函数 +func (t *Tensor) Sigmoid() *Tensor { + // 计算每个元素的sigmoid值 + inputShape := t.Data.Shape() + resultData := make([]float64, t.Data.Size()) + + // 遍历所有元素计算sigmoid + idx := 0 + if len(inputShape) == 1 { + for i := 0; i < inputShape[0]; i++ { + val, _ := t.Data.Get(i) + resultData[idx] = sigmoid(val) + idx++ + } + } else if len(inputShape) == 2 { + rows, cols := inputShape[0], inputShape[1] + for i := 0; i < rows; i++ { + for j := 0; j < cols; j++ { + val, _ := t.Data.Get(i, j) + resultData[idx] = sigmoid(val) + idx++ + } + } + } else if len(inputShape) == 4 { + batch, ch, h, w := inputShape[0], inputShape[1], inputShape[2], inputShape[3] + for b := 0; b < batch; b++ { + for c := 0; c < ch; c++ { + for h_idx := 0; h_idx < h; h_idx++ { + for w_idx := 0; w_idx < w; w_idx++ { + val, _ := t.Data.Get(b, c, h_idx, w_idx) + resultData[idx] = sigmoid(val) + idx++ + } + } + } + } + } + + result, _ := gomatrix.NewMatrix(resultData, inputShape) + + output := &Tensor{ + Data: result, + Op: "sigmoid", + } + + output.Prevs[0] = t + output.Num_Prevs = 1 + + output.backwardFunc = func() { + if t.Grad != nil { + // Sigmoid的导数: sigmoid(x) * (1 - sigmoid(x)) + gradData := make([]float64, t.Data.Size()) + gradIdx := 0 + + if len(inputShape) == 1 { + for i := 0; i < inputShape[0]; i++ { + sigmoidVal, _ := result.Get(i) + gradData[gradIdx] = sigmoidVal * (1 - sigmoidVal) + gradIdx++ + } + } else if len(inputShape) == 2 { + rows, cols := inputShape[0], inputShape[1] + for i := 0; i < rows; i++ { + for j := 0; j < cols; j++ { + sigmoidVal, _ := result.Get(i, j) + gradData[gradIdx] = sigmoidVal * (1 - sigmoidVal) + gradIdx++ + } + } + } else if len(inputShape) == 4 { + batch, ch, h, w := inputShape[0], inputShape[1], inputShape[2], inputShape[3] + for b := 0; b < batch; b++ { + for c := 0; c < ch; c++ { + for h_idx := 0; h_idx < h; h_idx++ { + for w_idx := 0; w_idx < w; w_idx++ { + sigmoidVal, _ := result.Get(b, c, h_idx, w_idx) + gradData[gradIdx] = sigmoidVal * (1 - sigmoidVal) + gradIdx++ + } + } + } + } + } + + gradMatrix, _ := gomatrix.NewMatrix(gradData, inputShape) + + // 与输出梯度相乘 + gradWithOutput, _ := gradMatrix.Multiply(output.Grad) + grad, _ := t.Grad.Add(gradWithOutput) + t.Grad = grad + } + } + + return output +} + +// ReLU 激活函数 +func (t *Tensor) ReLU() *Tensor { + // 计算每个元素的ReLU值 + inputShape := t.Data.Shape() + resultData := make([]float64, t.Data.Size()) + + // 遍历所有元素计算ReLU + idx := 0 + if len(inputShape) == 1 { + for i := 0; i < inputShape[0]; i++ { + val, _ := t.Data.Get(i) + if val > 0 { + resultData[idx] = val + } else { + resultData[idx] = 0 + } + idx++ + } + } else if len(inputShape) == 2 { + rows, cols := inputShape[0], inputShape[1] + for i := 0; i < rows; i++ { + for j := 0; j < cols; j++ { + val, _ := t.Data.Get(i, j) + if val > 0 { + resultData[idx] = val + } else { + resultData[idx] = 0 + } + idx++ + } + } + } else if len(inputShape) == 4 { + batch, ch, h, w := inputShape[0], inputShape[1], inputShape[2], inputShape[3] + for b := 0; b < batch; b++ { + for c := 0; c < ch; c++ { + for h_idx := 0; h_idx < h; h_idx++ { + for w_idx := 0; w_idx < w; w_idx++ { + val, _ := t.Data.Get(b, c, h_idx, w_idx) + if val > 0 { + resultData[idx] = val + } else { + resultData[idx] = 0 + } + idx++ + } + } + } + } + } + + result, _ := gomatrix.NewMatrix(resultData, inputShape) + + output := &Tensor{ + Data: result, + Op: "relu", + } + + output.Prevs[0] = t + output.Num_Prevs = 1 + + output.backwardFunc = func() { + if t.Grad != nil { + // ReLU的导数: 输入大于0时为1,否则为0 + gradData := make([]float64, t.Data.Size()) + gradIdx := 0 + + if len(inputShape) == 1 { + for i := 0; i < inputShape[0]; i++ { + val, _ := t.Data.Get(i) + if val > 0 { + gradData[gradIdx] = 1 + } else { + gradData[gradIdx] = 0 + } + gradIdx++ + } + } else if len(inputShape) == 2 { + rows, cols := inputShape[0], inputShape[1] + for i := 0; i < rows; i++ { + for j := 0; j < cols; j++ { + val, _ := t.Data.Get(i, j) + if val > 0 { + gradData[gradIdx] = 1 + } else { + gradData[gradIdx] = 0 + } + gradIdx++ + } + } + } else if len(inputShape) == 4 { + batch, ch, h, w := inputShape[0], inputShape[1], inputShape[2], inputShape[3] + for b := 0; b < batch; b++ { + for c := 0; c < ch; c++ { + for h_idx := 0; h_idx < h; h_idx++ { + for w_idx := 0; w_idx < w; w_idx++ { + val, _ := t.Data.Get(b, c, h_idx, w_idx) + if val > 0 { + gradData[gradIdx] = 1 + } else { + gradData[gradIdx] = 0 + } + gradIdx++ + } + } + } + } + } + + gradMatrix, _ := gomatrix.NewMatrix(gradData, inputShape) + + // 与输出梯度相乘 + gradWithOutput, _ := gradMatrix.Multiply(output.Grad) + grad, _ := t.Grad.Add(gradWithOutput) + t.Grad = grad + } + } + + return output +} + +// sigmoid 计算sigmoid函数值 +func sigmoid(x float64) float64 { + if x > 0 { + z := math.Exp(-x) + return 1 / (1 + z) + } else { + z := math.Exp(x) + return z / (1 + z) + } +} + // Backward 执行反向传播 func (t *Tensor) Backward() { if t.backwardFunc != nil {