Redes neurais para os menores

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 Network

Por 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:

  1. Converter imagem em vetor
  2. Converter vetores em matrizes
  3. Soma as matrizes e obtenha uma única matriz (W)
  4. Zere uma única matriz na diagonal
  5. Multiplique a matriz W pelo vetor da imagem de entrada
  6. Passe o vetor recebido através da função de ativação para esta rede (F)
  7. 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") // Add images to the array vectorArrays[0] = x1 vectorArrays[1] = x2 vectorArrays[2] = x3 

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 { // Activation Func "sigmod" S[i] = sigmod(element) } continue } else { S, _ = W.VectorMultiply(S) for i, element := range S { // Activation Func "sigmod" S[i] = sigmod( element) } } } 

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 é

Imagens de origem






Código inteiro
pacote 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) } 

Source: https://habr.com/ru/post/pt417063/


All Articles