138 lines
3.4 KiB
Go
138 lines
3.4 KiB
Go
package main
|
||
|
||
import (
|
||
"encoding/csv"
|
||
"fmt"
|
||
"math"
|
||
"os"
|
||
"path/filepath"
|
||
"strconv"
|
||
"time"
|
||
|
||
"gonum.org/v1/gonum/mat"
|
||
)
|
||
|
||
const DATA_DIR string = "data"
|
||
|
||
type layer struct {
|
||
weights *mat.Dense
|
||
biases *mat.Dense
|
||
}
|
||
|
||
func array_flatten(two_d_array [][]string) []float64 {
|
||
var result []float64
|
||
for _, line := range two_d_array {
|
||
for _, value := range line {
|
||
float_value, _ := strconv.ParseFloat(value, 64)
|
||
result = append(result, float_value)
|
||
}
|
||
}
|
||
return result
|
||
}
|
||
|
||
func load_matrix_from_csv(file_path string) (*mat.Dense, error) {
|
||
f, err := os.Open(file_path)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer f.Close()
|
||
data, err := csv.NewReader(f).ReadAll()
|
||
return mat.NewDense(len(data), len(data[0]), array_flatten(data)), err
|
||
}
|
||
|
||
func load_test_data(dir string) (*mat.Dense, []layer, error) {
|
||
inputs, err := load_matrix_from_csv(filepath.Join(dir, "inputs.csv"))
|
||
if err != nil { return nil, nil, err }
|
||
var layers []layer
|
||
var weights *mat.Dense
|
||
var biases *mat.Dense
|
||
var path string
|
||
|
||
for n := 1; n < 100; n++ {
|
||
path = filepath.Join(dir, fmt.Sprintf("weights%02d.csv", n))
|
||
_, err = os.Stat(path)
|
||
if err != nil { break }
|
||
weights, err = load_matrix_from_csv(path)
|
||
if err != nil { return nil, nil, err }
|
||
|
||
path = filepath.Join(dir, fmt.Sprintf("biases%02d.csv", n))
|
||
_, err = os.Stat(path)
|
||
if err != nil { break }
|
||
biases, err = load_matrix_from_csv(path)
|
||
if err != nil { return nil, nil, err }
|
||
|
||
layers = append(layers, layer{weights, biases})
|
||
}
|
||
return inputs, layers, nil
|
||
}
|
||
|
||
func sigmoid_scalar(x float64) float64 {
|
||
return 1 / (1 + math.Exp(-x))
|
||
}
|
||
|
||
func sigmoid_element(i, j int, value float64) float64 {
|
||
return sigmoid_scalar(value)
|
||
}
|
||
|
||
func sigmoid_matrix(x mat.Matrix) *mat.Dense {
|
||
var y mat.Dense
|
||
y.Apply(sigmoid_element, x)
|
||
return &y
|
||
}
|
||
|
||
func layer_func(inputs *mat.Dense, weights *mat.Dense, biases *mat.Dense, activation func(x mat.Matrix) *mat.Dense) *mat.Dense {
|
||
var output mat.Dense
|
||
output.Mul(weights, inputs)
|
||
output.Add(&output, biases)
|
||
return sigmoid_matrix(&output)
|
||
}
|
||
|
||
func feed_forward(x *mat.Dense, layers []layer) *mat.Dense {
|
||
var y *mat.Dense = x
|
||
for _, l := range layers {
|
||
y = layer_func(y, l.weights, l.biases, sigmoid_matrix)
|
||
}
|
||
return y
|
||
}
|
||
|
||
func time_feed_forward(n int) {
|
||
inputs, layers, _ := load_test_data(DATA_DIR)
|
||
t0 := time.Now()
|
||
for i := 0; i < n; i++ { feed_forward(inputs, layers) }
|
||
elapsed := time.Since(t0)
|
||
fmt.Printf("%.5f\n", elapsed.Seconds())
|
||
}
|
||
|
||
func examples() {
|
||
x := mat.NewDense(2, 1, []float64{0, 2})
|
||
w1 := mat.NewDense(2, 2, []float64{1, 0, 2, 4})
|
||
b1 := mat.NewDense(2, 1, []float64{0, 1})
|
||
w2 := mat.NewDense(2, 2, []float64{1, 0, 2, 4})
|
||
b2 := mat.NewDense(2, 1, []float64{-0.5, 1})
|
||
layers := []layer{{w1, b1}, {w2, b2}}
|
||
|
||
pre := " "
|
||
fmt.Printf("x1 = %v\n", mat.Formatted(x, mat.Prefix(pre)))
|
||
|
||
fmt.Printf("w1 = %v\n", mat.Formatted(w1, mat.Prefix(pre)))
|
||
fmt.Printf("b1 = %v\n", mat.Formatted(b1, mat.Prefix(pre)))
|
||
fmt.Println("σ(w1 * x1 + b1) = ")
|
||
x2 := layer_func(x, w1, b1, sigmoid_matrix)
|
||
fmt.Printf("x2 = %v\n\n", mat.Formatted(x2, mat.Prefix(pre)))
|
||
|
||
fmt.Printf("w2 = %v\n", mat.Formatted(w2, mat.Prefix(pre)))
|
||
fmt.Printf("b2 = %v\n", mat.Formatted(b2, mat.Prefix(pre)))
|
||
fmt.Println("σ(w2 * x2 + b2) = ")
|
||
x3 := feed_forward(x, layers)
|
||
fmt.Printf("x3 = %v\n", mat.Formatted(x3, mat.Prefix(pre)))
|
||
}
|
||
|
||
func main() {
|
||
n, err := strconv.Atoi(os.Args[1])
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
os.Exit(2)
|
||
}
|
||
time_feed_forward(n)
|
||
}
|