Face aux rĂ©seaux de neurones Ă l'universitĂ©, le rĂ©seau Hopfield est devenu l'un de mes prĂ©fĂ©rĂ©s. J'ai Ă©tĂ© surpris qu'il soit le dernier sur la liste des laboratoires, car son travail peut ĂȘtre clairement dĂ©montrĂ© Ă l'aide d'images et il n'est pas si difficile Ă mettre en Ćuvre.
Cet article montre comment résoudre le problÚme de restauration d'images déformées à l'aide d'un réseau de neurones Hopfield, préalablement formé sur des images de référence.
J'ai essayĂ© de dĂ©crire Ă©tape par Ă©tape et aussi simplement que possible le processus de mise en Ćuvre d'un programme qui vous permet de jouer avec un rĂ©seau de neurones directement dans le navigateur, d'entraĂźner le rĂ©seau Ă l'aide de mes propres images dessinĂ©es Ă la main et de tester son fonctionnement sur des images dĂ©formĂ©es. images.
Pour la mise en Ćuvre, vous aurez besoin de :
Le navigateur
Compréhension de base des réseaux de neurones
Connaissance de base de JavaScript / HTML
Un peu de théorie
Le rĂ©seau de neurones Hopfield est un rĂ©seau de neurones entiĂšrement connectĂ© avec une matrice symĂ©trique de connexions. Un tel rĂ©seau peut ĂȘtre utilisĂ© pour organiser la mĂ©moire associative, en tant que filtre, et aussi pour rĂ©soudre certains problĂšmes d'optimisation.
- . , . , , .
, , . (, ), . , , «» ( ).
:
:
â
â
-
-
- .
. :
( ):
â
;
â ;
â .
. â 3, , , . , .
.
Canvas , ( ) . , Canvas ( «» ).
, 10Ă10 . , , 100 ( 100 ). â , â1 1, â1 â , 1 â .
- , .
// 10
const gridSize = 10;
//
const squareSize = 45;
// (100)
const inputNodes = gridSize * gridSize;
// ,
//
let userImageState = [];
//
let isDrawing = false;
//
for (let i = 0; i < inputNodes; i += 1) {
userImageState[i] = -1;
}
// :
const userCanvas = document.getElementById('userCanvas');
const userContext = userCanvas.getContext('2d');
const netCanvas = document.getElementById('netCanvas');
const netContext = netCanvas.getContext('2d');
, .
//
// 100 (gridSize * gridSize)
const drawGrid = (ctx) => {
ctx.beginPath();
ctx.fillStyle = 'white';
ctx.lineWidth = 3;
ctx.strokeStyle = 'black';
for (let row = 0; row < gridSize; row += 1) {
for (let column = 0; column < gridSize; column += 1) {
const x = column * squareSize;
const y = row * squareSize;
ctx.rect(x, y, squareSize, squareSize);
ctx.fill();
ctx.stroke();
}
}
ctx.closePath();
};
«» , .
//
const handleMouseDown = (e) => {
userContext.fillStyle = 'black';
// x, y
// squareSize squareSize (4545 )
userContext.fillRect(
Math.floor(e.offsetX / squareSize) * squareSize,
Math.floor(e.offsetY / squareSize) * squareSize,
squareSize, squareSize,
);
// ,
//
const { clientX, clientY } = e;
const coords = getNewSquareCoords(userCanvas, clientX, clientY, squareSize);
const index = calcIndex(coords.x, coords.y, gridSize);
//
if (isValidIndex(index, inputNodes) && userImageState[index] !== 1) {
userImageState[index] = 1;
}
// ( )
isDrawing = true;
};
//
const handleMouseMove = (e) => {
// , .. ,
if (!isDrawing) return;
// , handleMouseDown
// isDrawing = true;
userContext.fillStyle = 'black';
userContext.fillRect(
Math.floor(e.offsetX / squareSize) * squareSize,
Math.floor(e.offsetY / squareSize) * squareSize,
squareSize, squareSize,
);
const { clientX, clientY } = e;
const coords = getNewSquareCoords(userCanvas, clientX, clientY, squareSize);
const index = calcIndex(coords.x, coords.y, gridSize);
if (isValidIndex(index, inputNodes) && userImageState[index] !== 1) {
userImageState[index] = 1;
}
};
, , getNewSquareCoords, calcIndex isValidIndex. .
//
//
const calcIndex = (x, y, size) => x + y * size;
// ,
const isValidIndex = (index, len) => index < len && index >= 0;
//
// , 0 9
const getNewSquareCoords = (canvas, clientX, clientY, size) => {
const rect = canvas.getBoundingClientRect();
const x = Math.ceil((clientX - rect.left) / size) - 1;
const y = Math.ceil((clientY - rect.top) / size) - 1;
return { x, y };
};
. .
const clearCurrentImage = () => {
// ,
//
drawGrid(userContext);
drawGrid(netContext);
userImageState = new Array(gridSize * gridSize).fill(-1);
};
«» .
â . ( ).
...
const weights = []; //
for (let i = 0; i < inputNodes; i += 1) {
weights[i] = new Array(inputNodes).fill(0); // 0
userImageState[i] = -1;
}
...
, , inputNodes . 100 , 100 .
( ) . . .
const memorizeImage = () => {
for (let i = 0; i < inputNodes; i += 1) {
for (let j = 0; j < inputNodes; j += 1) {
if (i === j) weights[i][j] = 0;
else {
// , userImageState
// -1 1, -1 - , 1 -
weights[i][j] += userImageState[i] * userImageState[j];
}
}
}
};
, , , . :
// - html lodash:
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
...
const recognizeSignal = () => {
let prevNetState;
// .
//
// (2 ),
const currNetState = [...userImageState];
do {
// ,
// ..
prevNetState = [...currNetState];
// 3
for (let i = 0; i < inputNodes; i += 1) {
let sum = 0;
for (let j = 0; j < inputNodes; j += 1) {
sum += weights[i][j] * prevNetState[j];
}
// ( - )
currNetState[i] = sum >= 0 ? 1 : -1;
}
//
// - isEqual
} while (!_.isEqual(currNetState, prevNetState));
// ( ),
drawImageFromArray(currNetState, netContext);
};
isEqual lodash.
drawImageFromArray. .
const drawImageFromArray = (data, ctx) => {
const twoDimData = [];
//
while (data.length) twoDimData.push(data.splice(0, gridSize));
//
drawGrid(ctx);
// ( )
for (let i = 0; i < gridSize; i += 1) {
for (let j = 0; j < gridSize; j += 1) {
if (twoDimData[i][j] === 1) {
ctx.fillStyle = 'black';
ctx.fillRect((j * squareSize), (i * squareSize), squareSize, squareSize);
}
}
}
};
HTML .
HTML
const resetButton = document.getElementById('resetButton');
const memoryButton = document.getElementById('memoryButton');
const recognizeButton = document.getElementById('recognizeButton');
//
resetButton.addEventListener('click', () => clearCurrentImage());
memoryButton.addEventListener('click', () => memorizeImage());
recognizeButton.addEventListener('click', () => recognizeSignal());
//
userCanvas.addEventListener('mousedown', (e) => handleMouseDown(e));
userCanvas.addEventListener('mousemove', (e) => handleMouseMove(e));
// ,
userCanvas.addEventListener('mouseup', () => isDrawing = false);
userCanvas.addEventListener('mouseleave', () => isDrawing = false);
//
drawGrid(userContext);
drawGrid(netContext);
, :
:
! .
, ,
(
â ). , , , , , .
Au lieu de la littérature, les conférences ont été utilisées par un excellent professeur sur les réseaux de neurones - Sergei Mikhailovich Roshchin , pour lequel un grand merci à lui.