package main

import (
	"encoding/csv"
	"fmt"
	"log"
	"math/rand"
	"os"
	"strconv"
	"time"

	"gorgonia.org/gorgonia"
	"gorgonia.org/tensor"
)

const (
	vocabSize     = 34    // Количество уникальных символов (например, русский алфавит + пробел)
	embedSize     = 16    // Размер векторного представления
	numIterations = 10000 // Количество итераций для обучения
)

func main() {
	rand.Seed(time.Now().UnixNano())

	g := gorgonia.NewGraph()

	// Создание весов и смещений для модели
	wEmbedding := gorgonia.NewMatrix(g, tensor.Float64, gorgonia.WithShape(vocabSize, embedSize), gorgonia.WithInit(gorgonia.GlorotN(1.0)), gorgonia.WithName("wEmbedding"))
	wOut := gorgonia.NewMatrix(g, tensor.Float64, gorgonia.WithShape(embedSize, vocabSize), gorgonia.WithInit(gorgonia.GlorotN(1.0)), gorgonia.WithName("wOut"))

	// Ввод режима от пользователя
	var mode string
	fmt.Print("Введите режим (train/test): ")
	fmt.Scan(&mode)

	switch mode {
	case "train":
		train(g, wEmbedding, wOut)
		if err := saveWeightsToCSV("model_weights.csv", wEmbedding, wOut); err != nil {
			log.Fatalf("Ошибка сохранения весов: %v", err)
		}
		fmt.Println("Модель обучена и веса сохранены в model_weights.csv")

	case "test":
		if err := loadWeightsFromCSV("model_weights.csv", wEmbedding, wOut); err != nil {
			log.Fatalf("Ошибка загрузки весов: %v", err)
		}
		test(g, wEmbedding, wOut)

	default:
		fmt.Println("Неверный режим, используйте 'train' или 'test'")
	}
}

func train(g *gorgonia.ExprGraph, wEmbedding, wOut *gorgonia.Node) {
	x := gorgonia.NewMatrix(g, tensor.Float64, gorgonia.WithShape(1, vocabSize), gorgonia.WithName("x"))

	hidden := gorgonia.Must(gorgonia.Mul(x, wEmbedding))
	out := gorgonia.Must(gorgonia.Mul(hidden, wOut))
	output := gorgonia.Must(gorgonia.SoftMax(out))

	vm := gorgonia.NewTapeMachine(g)
	defer vm.Close()

	for i := 0; i < numIterations; i++ {
		inputData := make([]float64, vocabSize)
		inputData[rand.Intn(vocabSize)] = 1 // Случайный вход

		if err := gorgonia.Let(x, tensor.New(tensor.WithBacking(inputData), tensor.WithShape(1, vocabSize))); err != nil {
			log.Fatal(err)
		}

		if err := vm.RunAll(); err != nil {
			log.Fatal(err)
		}

		result := output.Value().Data().([]float64)
		predictedChar := getPredictedChar(result)
		fmt.Printf("Обучение, итерация %d, предсказание символа: '%s'\n", i+1, predictedChar)

		vm.Reset()
	}
}

func test(g *gorgonia.ExprGraph, wEmbedding, wOut *gorgonia.Node) {
	x := gorgonia.NewMatrix(g, tensor.Float64, gorgonia.WithShape(1, vocabSize), gorgonia.WithName("x"))

	hidden := gorgonia.Must(gorgonia.Mul(x, wEmbedding))
	out := gorgonia.Must(gorgonia.Mul(hidden, wOut))
	output := gorgonia.Must(gorgonia.SoftMax(out))

	vm := gorgonia.NewTapeMachine(g)
	defer vm.Close()

	testData := []string{"П", "Р", "И", "В", "Е", "Т"}

	for _, inputChar := range testData {
		inputData := make([]float64, vocabSize)
		inputIndex := int(rune(inputChar[0]) - 'А')

		if inputIndex >= 0 && inputIndex < vocabSize {
			inputData[inputIndex] = 1
		}

		if err := gorgonia.Let(x, tensor.New(tensor.WithBacking(inputData), tensor.WithShape(1, vocabSize))); err != nil {
			log.Fatal(err)
		}

		if err := vm.RunAll(); err != nil {
			log.Fatal(err)
		}

		result := output.Value().Data().([]float64)
		predictedChar := getPredictedChar(result)

		fmt.Printf("Тестирование на входе '%s' => предсказание: '%s'\n", inputChar, predictedChar)

		vm.Reset()
	}
}

func getPredictedChar(results []float64) string {
	maxIdx := 0
	maxVal := results[0]
	for idx, val := range results {
		if val > maxVal {
			maxIdx = idx
			maxVal = val
		}
	}
	return string('А' + rune(maxIdx))
}

func saveWeightsToCSV(filename string, wEmbedding, wOut *gorgonia.Node) error {
	file, err := os.Create(filename)
	if err != nil {
		return fmt.Errorf("ошибка создания файла для записи весов: %v", err)
	}
	defer file.Close()

	writer := csv.NewWriter(file)
	defer writer.Flush()

	if err := writeMatrix(writer, wEmbedding.Value().Data().([]float64), vocabSize, embedSize); err != nil {
		return fmt.Errorf("ошибка записи матрицы wEmbedding: %v", err)
	}

	if err := writeMatrix(writer, wOut.Value().Data().([]float64), embedSize, vocabSize); err != nil {
		return fmt.Errorf("ошибка записи матрицы wOut: %v", err)
	}

	return nil
}

func writeMatrix(writer *csv.Writer, data []float64, rows, cols int) error {
	for i := 0; i < rows; i++ {
		row := make([]string, cols)
		for j := 0; j < cols; j++ {
			row[j] = fmt.Sprintf("%f", data[i*cols+j])
		}
		if err := writer.Write(row); err != nil {
			return err
		}
	}
	return nil
}

func loadWeightsFromCSV(filename string, wEmbedding, wOut *gorgonia.Node) error {
	file, err := os.Open(filename)
	if err != nil {
		return fmt.Errorf("ошибка открытия файла с весами: %v", err)
	}
	defer file.Close()

	reader := csv.NewReader(file)

	// Чтение первой матрицы
	wEmbeddingData, err := readMatrix(reader, vocabSize, embedSize)
	if err != nil {
		return fmt.Errorf("ошибка чтения данных wEmbedding: %v", err)
	}

	// Чтение второй матрицы
	wOutData, err := readMatrix(reader, embedSize, vocabSize)
	if err != nil {
		return fmt.Errorf("ошибка чтения данных wOut: %v", err)
	}

	if err = assignFromReadData(wEmbedding, wEmbeddingData); err != nil {
		return fmt.Errorf("ошибка назначения данных wEmbedding: %v", err)
	}

	if err = assignFromReadData(wOut, wOutData); err != nil {
		return fmt.Errorf("ошибка назначения данных wOut: %v", err)
	}

	return nil
}

func readMatrix(reader *csv.Reader, rows, cols int) ([]float64, error) {
	data := make([]float64, rows*cols)
	for i := 0; i < rows; i++ {
		record, err := reader.Read()
		if err != nil {
			return nil, err
		}
		if len(record) != cols {
			return nil, fmt.Errorf("неверный формат строки: ожидаемые колонки %d, получено %d", cols, len(record))
		}
		for j := 0; j < cols; j++ {
			data[i*cols+j], err = strconv.ParseFloat(record[j], 64)
			if err != nil {
				return nil, err
			}
		}
	}
	return data, nil
}

func assignFromReadData(node *gorgonia.Node, data []float64) error {
	return gorgonia.Let(node, tensor.New(tensor.WithBacking(data), tensor.WithShape(node.Shape()...)))
}


исправь ошибку

Введите режим (train/test): test
2024/10/14 21:29:38 Ошибка загрузки весов: ошибка чтения данных wOut: record on line 35: wrong number of fields


понлостью перепиши загрузку и сохраннеите данных в ИИ