Olá, neste exemplo, quero mostrar como você pode implementar uma rede Hopfield para reconhecimento de padrões.
Eu mesmo, como muitos em um dia, decidi me interessar pelo treinamento de programas, IA e redes neurais. Felizmente, existem muitas análises e exemplos na rede, mas todas operam com uma abundância de fórmulas de funções e, se você não é proficiente em matemática (como eu), tentarei demonstrar um exemplo simples da rede Hopfield usando a linguagem Golang (GO).
Descrição matemática da rede -
Hopfield NetworkPor que exatamente a rede Hopfield?
Um exemplo bastante rápido e mais ou menos claro, se você pode operar com termos como "simples" e "compreensível" no mundo da IA.
Neste exemplo, tentaremos reconhecer imagens de uma imagem em preto e branco medindo 20x20 pixels.
Vamos tentar entender as etapas que precisamos concluir antes de obter o resultado desejado:
- Converter imagem em vetor
- Converter vetores em matrizes
- Soma as matrizes e obtenha uma única matriz (W)
- Zere uma única matriz na diagonal
- Multiplique a matriz W pelo vetor da imagem de entrada
- Passe o vetor recebido através da função de ativação para esta rede (F)
- Substitua o novo vetor na etapa 5 e continue a operação até obtermos um estado estável da rede (obteremos o mesmo vetor na saída).
Vamos seguir para o código com uma descrição detalhada. Todas as bibliotecas que precisamos:
package main import ( "github.com/oelmekki/matrix" "fmt" "os" "log" "image" "math" "image/color" "image/png" _ "image/jpeg" )
Criamos uma matriz de vetores de 3 elementos (o número de amostras), convertemos imagens em vetores e adicionamos amostras à matriz. O vetor 'Y' é a imagem que queremos reconhecer.
vectorArrays := [3][]float64{} x1 := getVectorFromImage("Images/.jpg") x2 := getVectorFromImage("Images/.jpg") x3 := getVectorFromImage("Images/.jpg") y := getVectorFromImage("Images/Income.jpg")
Crie uma matriz de matrizes, converta todos os vetores em matrizes e adicione-os à matriz de matrizes. Criamos um espaço em branco da matriz W e começamos a somar todas as matrizes, colocando o resultado em W.
matrixArray := [len(vectorArrays)]matrix.Matrix{} for i, vInArray := range vectorArrays { matrixArray[i] = vectorToMatrix(vInArray, vInArray) } W := matrix.Matrix{} for i, matrixInArray := range matrixArray { if i == 0 { W = matrixInArray continue } W, _ = W.Add(matrixInArray) }
Zere a matriz na diagonal.
for i := 0; i < W.Rows(); i++ { W.SetAt(i, i, 0) }
Criamos um espaço em branco do vetor de saída, multiplicamos a matriz pelo vetor Y, colocamos o resultado no vetor S e o substituímos por multiplicação.
S := make([]float64, 400) for II := 0; II < 100; II++ { if II == 0 { S, _ = W.VectorMultiply(y) for i, element := range S {
Mais sobre a função sigmod ().
Essa é a função de ativação F e o princípio de sua operação (no nosso exemplo) é converter os dados no vetor de saída e trazê-los para 1 ou -1.
Como trabalhamos com uma rede bipolar, os dados podem ser apenas 1 e -1.
A imagem deve ser reduzida de RGB para 1 e -1, onde a soma de todos os pontos divididos por 3 (brilho condicional do pixel) deve tender para preto ou branco. Como R = 255, G = 255, B = 255 é branco e R = 0, G = 0, B = 0 é preto. Eu escolhi um limite de 150, para que mais ou igual a 150 seja branco (1) tudo menos que preto (-1), onde a escolha entre preto em -1 e branco em 1 pode ser condicional, tudo o que precisamos é decompor preto e branco por valores. O branco também pode ser -1 e o preto 1; nesse caso, não importa. Também vale a pena considerar que estamos trabalhando com uma matriz simétrica e as imagens devem ser equilaterais.
Para converter a imagem em um vetor, você precisa representar a imagem como uma matriz que cortamos horizontalmente e adicionamos cada camada de corte (e teremos 20 delas) ao final da camada anterior e obtemos um vetor 400 (20x20).
No exemplo, não verifico o vetor de saída quanto à estabilidade, mas simplesmente passo o ciclo 100 vezes e, no final, verifico com quais amostras nosso resultado se parece. A rede adivinha ou não, ao mesmo tempo, distribuindo a chamada quimera ou uma interpretação livre do que ela podia ver. Este é o resultado que eu salvo na imagem.
Como usamos o modo de rede síncrona Hopfield, o resultado será fraco. Obviamente, você pode usar assíncrono, o que levará mais tempo e recursos, mas o resultado será muito melhor.
Exemplo de trabalho:
Imagem de entrada -

A resposta é

Código inteiropacote principal
package main import ( "github.com/oelmekki/matrix" "fmt" "os" "log" "image" "math" "image/color" "image/png" _ "image/jpeg" ) func vectorToMatrix(v1 []float64, v2 []float64) (matrix.Matrix) { m := matrix.GenerateMatrix(len(v1), len(v2)) for i, elem := range v1 { for i2, elem2 := range v1 { m.SetAt(i, i2, elem2*elem) } } return m } func sigmod(v float64) (float64) { if v >= 0 { return 1 } else { return -1 } } func getVectorFromImage(path string) ([] float64) { reader, err := os.Open(path) if err != nil { log.Fatal(err) } defer reader.Close() m, _, err := image.Decode(reader) if err != nil { log.Fatal(err) } v := make([]float64, 400) vectorIteration := 0 for x := 0; x < 20; x++ { for y := 0; y < 20; y++ { r, g, b, _ := m.At(x, y).RGBA() normalVal := float64(r+g+b) / 3 / 257 if normalVal >= 150 { v[vectorIteration] = 1 } else { v[vectorIteration] = -1 } vectorIteration ++ } } return v } func main() { fmt.Println("Memory size ~ ", int(400/(2*math.Log2(400))), " objects") fmt.Println("1 - ") fmt.Println("2 - ") fmt.Println("3 - ") fmt.Println("-----Start------") vectorArrays := [3][]float64{} x1 := getVectorFromImage("Images/.jpg") x2 := getVectorFromImage("Images/.jpg") x3 := getVectorFromImage("Images/.jpg") y := getVectorFromImage("Images/Income.jpg") vectorArrays[0] = x1 vectorArrays[1] = x2 vectorArrays[2] = x3 matrixArray := [len(vectorArrays)]matrix.Matrix{} for i, vInArray := range vectorArrays { matrixArray[i] = vectorToMatrix(vInArray, vInArray) } W := matrix.Matrix{} for i, matrixInArray := range matrixArray { if i == 0 { W = matrixInArray continue } W, _ = W.Add(matrixInArray) } for i := 0; i < W.Rows(); i++ { W.SetAt(i, i, 0) } S := make([]float64, 400) for II := 0; II < 100; II++ { if II == 0 { S, _ = W.VectorMultiply(y) for i, element := range S { S[i] = sigmod(element) } continue } else { S, _ = W.VectorMultiply(S) for i, element := range S { S[i] = sigmod(element) } } } ar := [3]int{1, 1, 1} for vectorI, v := range vectorArrays { for i, elem := range v { if elem != S[i] { ar[vectorI] = 0 break } } } for i, el := range ar { if el == 1 { fmt.Println("Looks like", i+1) } } img := image.NewRGBA(image.Rect(0, 0, 20, 20)) xx := 0 yy := 0 for i := 0; i < 400; i++ { if i%20 == 0 { yy++ xx = 0 } else { xx++ } if S[i] == -1 { img.Set(xx, yy, color.RGBA{0, 0, 0, 255}) } else { img.Set(xx, yy, color.RGBA{255, 255, 255, 255}) } } f, _ := os.OpenFile("Images/out.png", os.O_WRONLY|os.O_CREATE, 0600) png.Encode(f, img) f.Close() var str string fmt.Scanln(&str) }