Как стать автором
Обновить

Небольшая нейронная сеть на Raspberry Pi Pico

Уровень сложностиПростой
Время на прочтение3 мин
Количество просмотров7.6K

Всем читателям привет! P.S Это мой первый пост, поэтому сильно не судите

В этом посте я вам покажу как создать достаточно эффективную нейронную сеть на Raspberry Pi Pico!

Для начала каким-либо образом копируем этот код в память pico:

from random import random
from math import exp

class NeyroNet:
	def __init__(self, n_inputs, hiddens_layer, n_outputs):
		self.network = list()
		
		self.n_outputs = n_outputs
	
		hidden_layer = [{'weights':[random() for i in range(n_inputs + 1)]} for i in range(hiddens_layer[0])]
		self.network.append(hidden_layer)
		
		for layer in hiddens_layer[1:]:
			hidden_layer = [{'weights':[random() for i in range(len(self.network[-1])+1)]} for i in range(layer)]
			self.network.append(hidden_layer)
			
		output_layer = [{'weights':[random() for i in range(hiddens_layer[-1] + 1)]} for i in range(n_outputs)]
		self.network.append(output_layer)
	
	def activate(self, weights, inputs):
		activation = weights[-1]
		for i in range(len(weights)-1):
			activation += weights[i] * inputs[i]
		return activation
	
	def transfer(self, activation):
		return 1.0 / (1.0 + exp(-activation))
	
	def forward_propagate(self, row):
		inputs = row
		for layer in self.network:
			new_inputs = []
			for neuron in layer:
				activation = self.activate(neuron['weights'], inputs)
				neuron['output'] = self.transfer(activation)
				new_inputs.append(neuron['output'])
			inputs = new_inputs
		return inputs
	
	def transfer_derivative(self, output):
		return output * (1.0 - output)
	
	def backward_propagate_error(self, expected):
		for i in reversed(range(len(self.network))):
			layer = self.network[i]
			errors = list()
			if i != len(self.network)-1:
				for j in range(len(layer)):
					error = 0.0
					for neuron in self.network[i + 1]:
						error += (neuron['weights'][j] * neuron['delta'])
					errors.append(error)
			else:
				for j in range(len(layer)):
					neuron = layer[j]
					errors.append(expected[j] - neuron['output'])
			for j in range(len(layer)):
				neuron = layer[j]
				neuron['delta'] = errors[j] * self.transfer_derivative(neuron['output'])
	
	def update_weights(self, row, l_rate):
		for i in range(len(self.network)):
			inputs = row[:-1]
			if i != 0:
				inputs = [neuron['output'] for neuron in self.network[i - 1]]
			for neuron in self.network[i]:
				for j in range(len(inputs)):
					neuron['weights'][j] += l_rate * neuron['delta'] * inputs[j]
				neuron['weights'][-1] += l_rate * neuron['delta']
	
	def train_network(self, train, l_rate, n_epoch, err_val_threshold=0):
		for epoch in range(n_epoch):
			sum_error = 0
			for row in train:
				outputs = self.forward_propagate(row)
				expected = [0 for i in range(self.n_outputs)]
				expected[row[-1]] = 1
				sum_error += sum([(expected[i]-outputs[i])**2 for i in range(len(expected))])
				self.backward_propagate_error(expected)
				self.update_weights(row, l_rate)
			pdat = '>epoch=%d, lrate=%.1f, error=%.10f' % (epoch, l_rate, sum_error)
			print(pdat, end='\r')
			if(sum_error < err_val_threshold):
				print(' ' * len(pdat), end='\r')
				print('THRESHOLD WITH EPOCH > ' + str(epoch))
				break
	
	def predict(self, row):
		outputs = self.forward_propagate(row)
		return outputs.index(max(outputs))

	def save(self, filename):
		with open(filename, 'w') as sv:
			sv.write(str(self.network))
			sv.close()

	def load(self, filename):
		with open(filename, 'r') as sv:
			data = sv.read()
			sv.close()
		self.network = eval(data)

Называем файл как угодно

Далее заходим в консоль pico через minicom или через thonny

Прописываем для инициализации нейронной сети

import <скопированный файл без приставки .py>
net = NeyroNet(3, [6], 2) # 3 - Сколько входов, [6] - Это список слоёв (в первом слое 6 нейронов), 2 - сколько выходов

Далее берём любой dataset

dataset = [[1, 0 ,0 ,1], [1, 1, 0, 1], [0, 1, 1, 0], [0, 0, 1, 0]]

Вы спросите: "Почему у тебя в каждом элементе по 4 значения а входов всего 4?"

Дело в том что самое последнее значение в элементе это и есть выходные данные, а все остальные - это входные!

После этого обучаем нейросеть

net.train_network(dataset, l_rate=0.5, n_epoch=10000, err_val_threshold=0.0009)

P.S. параметр err_val_threshold нужен для того чтобы не ждать пока нейросеть пройдёт все эпохи, этот параметр указывает пороговое значение ошибки т.e. если ошибка будет меньше этого значения обучение прекратится!

Ну вот мы и обучили нейросеть! Теперь давайте проверим что она выдаёт!

Как мы помним у нас всего выходных класса

net.predict([1, 0, 1]) # возвращает 1 т.е какой выход (начинается с 0)
net.forward_propagate([0, 0, 0]) # возвращает [0.99998464, 0.00006544]

И чтобы сохранить текущие веса нужно прописать:

net.save('<имя файла>')

А чтобы загрузить веса сети нужно воопервых при создании объекта сети нужно указать столько же выходов, сколько и было сохранено, а иначе будет ошибка!

net.load('<имя файла>')

Вот и всё!

Надеюсь вам понравилась статья!

Теги:
Хабы:
Всего голосов 11: ↑3 и ↓8-5
Комментарии7

Публикации

Истории

Работа

Python разработчик
136 вакансий
Data Scientist
60 вакансий

Ближайшие события