Introducción a los algoritmos y códigos de redes neuronales

Contenido del artículo

  • Perceptrón ( Perceptrón )
  • Algoritmo de propagación hacia atrás (algoritmo de propagación hacia atrás)
  • Red RBF (función de base radial, función de base radial): una red de alimentación directa de una sola capa que utiliza la base radial como función de activación de las neuronas de la capa oculta
  • Red ART (Teoría de resonancia adaptativa, Teoría de resonancia adaptativa): Estrategia de aprendizaje no supervisado de aprendizaje competitivo
  • Red SOM (Self Organizing Map, mapa autoorganizado): red neuronal no supervisada de aprendizaje competitivo
  • Redes de correlación en cascada: redes adaptativas estructurales
  • Elman Networks : Redes Recurrentes
  • máquina de Boltzmann

1. Perceptrón

1. Significado

Un perceptrón tiene los siguientes componentes:

  • Peso de entrada: un perceptrón puede recibir múltiples entradas x, cada entrada tiene un peso w , y también hay un elemento de sesgo b , que es el de la figura anterior.
  • Función de activación: como función de paso (función de paso), función sigmoide
  • Salida: y= f(wx+b)
  • Actualización de peso: w_i\flecha izquierda w_i + \Delta w_i, donde \Delta w_i = \eta (y-\hat{y})x_iη es la tasa de aprendizaje

Los perceptrones de una sola capa solo pueden resolver problemas linealmente separables. Un perceptrón multicapa puede ajustarse a cualquier función lineal, y cualquier problema de clasificación lineal o regresión lineal puede resolverse con un perceptrón.

2. Método de actualización

inserte la descripción de la imagen aquí

3. Código

class Perceptron(object):
    def __init__(self, input_num, activator):
        '''
        初始化感知器,设置输入参数的个数,以及激活函数。
        激活函数的类型为double -> double
        '''
        self.activator = activator
        # 权重向量初始化为0
        self.weights = [0.0 for _ in range(input_num)]
        # 偏置项初始化为0
        self.bias = 0.0
    def __str__(self):
        '''
        打印学习到的权重、偏置项
        '''
        return 'weights\t:%s\nbias\t:%f\n' % (self.weights, self.bias)
    def predict(self, input_vec):
        '''
        输入向量,输出感知器的计算结果
        '''
        # 把input_vec[x1,x2,x3...]和weights[w1,w2,w3,...]打包在一起
        # 变成[(x1,w1),(x2,w2),(x3,w3),...]
        # 然后利用map函数计算[x1*w1, x2*w2, x3*w3]
        # 最后利用reduce求和
        return self.activator(
            reduce(lambda a, b: a + b,
                   map(lambda (x, w): x * w,  
                       zip(input_vec, self.weights))
                , 0.0) + self.bias)
    def train(self, input_vecs, labels, iteration, rate):
        '''
        输入训练数据:一组向量、与每个向量对应的label;以及训练轮数、学习率
        '''
        for i in range(iteration):
            self._one_iteration(input_vecs, labels, rate)
    def _one_iteration(self, input_vecs, labels, rate):
        '''
        一次迭代,把所有的训练数据过一遍
        '''
        # 把输入和输出打包在一起,成为样本的列表[(input_vec, label), ...]
        # 而每个训练样本是(input_vec, label)
        samples = zip(input_vecs, labels)
        # 对每个样本,按照感知器规则更新权重
        for (input_vec, label) in samples:
            # 计算感知器在当前权重下的输出
            output = self.predict(input_vec)
            # 更新权重
            self._update_weights(input_vec, output, label, rate)
    def _update_weights(self, input_vec, output, label, rate):
        '''
        按照感知器规则更新权重
        '''
        # 把input_vec[x1,x2,x3,...]和weights[w1,w2,w3,...]打包在一起
        # 变成[(x1,w1),(x2,w2),(x3,w3),...]
        # 然后利用感知器规则更新权重
        delta = label - output
        self.weights = map(
            lambda (x, w): w + rate * delta * x,
            zip(input_vec, self.weights))
        # 更新bias
        self.bias += rate * delta

Entrena y predice:

def f(x):
    '''
    定义激活函数f
    '''
    return 1 if x > 0 else 0
def get_training_dataset():
    '''
    基于and真值表构建训练数据
    '''
    # 构建训练数据
    # 输入向量列表
    input_vecs = [[1,1], [0,0], [1,0], [0,1]]
    # 期望的输出列表,注意要与输入一一对应
    # [1,1] -> 1, [0,0] -> 0, [1,0] -> 0, [0,1] -> 0
    labels = [1, 0, 0, 0]
    return input_vecs, labels    
def train_and_perceptron():
    '''
    使用and真值表训练感知器
    '''
    # 创建感知器,输入参数个数为2(因为and是二元函数),激活函数为f
    p = Perceptron(2, f)
    # 训练,迭代10轮, 学习速率为0.1
    input_vecs, labels = get_training_dataset()
    p.train(input_vecs, labels, 10, 0.1)
    #返回训练好的感知器
    return p
if __name__ == '__main__': 
    # 训练and感知器
    and_perception = train_and_perceptron()
    # 打印训练获得的权重
    print and_perception
    # 测试
    print '1 and 1 = %d' % and_perception.predict([1, 1])
    print '0 and 0 = %d' % and_perception.predict([0, 0])
    print '1 and 0 = %d' % and_perception.predict([1, 0])
    print '0 and 1 = %d' % and_perception.predict([0, 1])

2. Algoritmo de propagación hacia atrás (BP)

1. Contenido

  • pesos y sesgos
  • Función de costo (función de costo): error cuadrático medio
  • Actualización de peso : estrategia de descenso de gradiente
  • Más métodos de optimización: "regularización", agregando una parte a la función objetivo de error que describe la complejidad de la red (como w²)

2. Cálculo y actualización de parámetros

2.1 Error total

2.2 El error se propaga hacia atrás en la capa oculta (capa de salida -> capa oculta)

Tomando el parámetro de peso w5 como ejemplo, si queremos saber cuánta influencia tiene w5 en el error total, podemos usar el error total para calcular la derivada parcial de w5: (regla de la cadena) 

Encuentre las tres derivadas parciales :

 

 Entonces, obtenemos:

El símbolo δ se puede utilizar para simplificar la fórmula (opcional):

Finalmente actualice el peso w:

2.3 Propagación inversa de errores (capa oculta -> capa oculta)

El método es similar, pero el error se calcula en función del error anterior pasado en:

El significado de δ en esta figura es diferente, solo representa el error (target-out)

out(h1) aceptará los errores de E(o1) y E(o2), por lo que ambos deben calcularse en este lugar.

Finalmente, actualice el peso w1:

Resumen, diagrama de descripción de la secuencia de propagación hacia adelante y hacia atrás: 

3. Actualización de gradiente estocástico y actualización por lotes

Al actualizar lotes, Δw se actualiza después de calcular un lote de muestras:

inserte la descripción de la imagen aquí

Comparación del descenso de gradiente por lotes y el descenso de gradiente estocástico

El algoritmo de descenso de gradiente Mini-Batch es un algoritmo entre el algoritmo de descenso de gradiente por lotes y el algoritmo de descenso de gradiente estocástico. Los parámetros se actualizan cada vez que se calcula una instancia de entrenamiento constante.

En cada iteración del algoritmo de descenso de gradiente por lotes, tenemos que usar todas las m muestras, en el descenso de gradiente estocástico solo necesitamos una muestra por iteración. Específicamente, el algoritmo de mini lotes usa b muestras para cada iteración, y este b se denomina parámetro de tamaño de mini lote.

3. Código

#coding:utf-8
import random
import math

#
#   参数解释:
#   "pd_" :偏导的前缀
#   "d_" :导数的前缀
#   "w_ho" :隐含层到输出层的权重系数索引
#   "w_ih" :输入层到隐含层的权重系数的索引

class NeuralNetwork:
    LEARNING_RATE = 0.5

    def __init__(self, num_inputs, num_hidden, num_outputs, hidden_layer_weights = None, hidden_layer_bias = None, output_layer_weights = None, output_layer_bias = None):
        self.num_inputs = num_inputs

        self.hidden_layer = NeuronLayer(num_hidden, hidden_layer_bias)
        self.output_layer = NeuronLayer(num_outputs, output_layer_bias)

        self.init_weights_from_inputs_to_hidden_layer_neurons(hidden_layer_weights)
        self.init_weights_from_hidden_layer_neurons_to_output_layer_neurons(output_layer_weights)

    def init_weights_from_inputs_to_hidden_layer_neurons(self, hidden_layer_weights):
        weight_num = 0
        for h in range(len(self.hidden_layer.neurons)):
            for i in range(self.num_inputs):
                if not hidden_layer_weights:
                    self.hidden_layer.neurons[h].weights.append(random.random())
                else:
                    self.hidden_layer.neurons[h].weights.append(hidden_layer_weights[weight_num])
                weight_num += 1

    def init_weights_from_hidden_layer_neurons_to_output_layer_neurons(self, output_layer_weights):
        weight_num = 0
        for o in range(len(self.output_layer.neurons)):
            for h in range(len(self.hidden_layer.neurons)):
                if not output_layer_weights:
                    self.output_layer.neurons[o].weights.append(random.random())
                else:
                    self.output_layer.neurons[o].weights.append(output_layer_weights[weight_num])
                weight_num += 1

    def inspect(self):
        print('------')
        print('* Inputs: {}'.format(self.num_inputs))
        print('------')
        print('Hidden Layer')
        self.hidden_layer.inspect()
        print('------')
        print('* Output Layer')
        self.output_layer.inspect()
        print('------')

    def feed_forward(self, inputs):
        hidden_layer_outputs = self.hidden_layer.feed_forward(inputs)
        return self.output_layer.feed_forward(hidden_layer_outputs)

    def train(self, training_inputs, training_outputs):
        self.feed_forward(training_inputs)

        # 1. 输出神经元的值
        pd_errors_wrt_output_neuron_total_net_input = [0] * len(self.output_layer.neurons)
        for o in range(len(self.output_layer.neurons)):

            # ∂E/∂zⱼ
            pd_errors_wrt_output_neuron_total_net_input[o] = self.output_layer.neurons[o].calculate_pd_error_wrt_total_net_input(training_outputs[o])

        # 2. 隐含层神经元的值
        pd_errors_wrt_hidden_neuron_total_net_input = [0] * len(self.hidden_layer.neurons)
        for h in range(len(self.hidden_layer.neurons)):

            # dE/dyⱼ = Σ ∂E/∂zⱼ * ∂z/∂yⱼ = Σ ∂E/∂zⱼ * wᵢⱼ
            d_error_wrt_hidden_neuron_output = 0
            for o in range(len(self.output_layer.neurons)):
                d_error_wrt_hidden_neuron_output += pd_errors_wrt_output_neuron_total_net_input[o] * self.output_layer.neurons[o].weights[h]

            # ∂E/∂zⱼ = dE/dyⱼ * ∂zⱼ/∂
            pd_errors_wrt_hidden_neuron_total_net_input[h] = d_error_wrt_hidden_neuron_output * self.hidden_layer.neurons[h].calculate_pd_total_net_input_wrt_input()

        # 3. 更新输出层权重系数
        for o in range(len(self.output_layer.neurons)):
            for w_ho in range(len(self.output_layer.neurons[o].weights)):

                # ∂Eⱼ/∂wᵢⱼ = ∂E/∂zⱼ * ∂zⱼ/∂wᵢⱼ
                pd_error_wrt_weight = pd_errors_wrt_output_neuron_total_net_input[o] * self.output_layer.neurons[o].calculate_pd_total_net_input_wrt_weight(w_ho)

                # Δw = α * ∂Eⱼ/∂wᵢ
                self.output_layer.neurons[o].weights[w_ho] -= self.LEARNING_RATE * pd_error_wrt_weight

        # 4. 更新隐含层的权重系数
        for h in range(len(self.hidden_layer.neurons)):
            for w_ih in range(len(self.hidden_layer.neurons[h].weights)):

                # ∂Eⱼ/∂wᵢ = ∂E/∂zⱼ * ∂zⱼ/∂wᵢ
                pd_error_wrt_weight = pd_errors_wrt_hidden_neuron_total_net_input[h] * self.hidden_layer.neurons[h].calculate_pd_total_net_input_wrt_weight(w_ih)

                # Δw = α * ∂Eⱼ/∂wᵢ
                self.hidden_layer.neurons[h].weights[w_ih] -= self.LEARNING_RATE * pd_error_wrt_weight

    def calculate_total_error(self, training_sets):
        total_error = 0
        for t in range(len(training_sets)):
            training_inputs, training_outputs = training_sets[t]
            self.feed_forward(training_inputs)
            for o in range(len(training_outputs)):
                total_error += self.output_layer.neurons[o].calculate_error(training_outputs[o])
        return total_error

class NeuronLayer:
    def __init__(self, num_neurons, bias):

        # 同一层的神经元共享一个截距项b
        self.bias = bias if bias else random.random()

        self.neurons = []
        for i in range(num_neurons):
            self.neurons.append(Neuron(self.bias))

    def inspect(self):
        print('Neurons:', len(self.neurons))
        for n in range(len(self.neurons)):
            print(' Neuron', n)
            for w in range(len(self.neurons[n].weights)):
                print('  Weight:', self.neurons[n].weights[w])
            print('  Bias:', self.bias)

    def feed_forward(self, inputs):
        outputs = []
        for neuron in self.neurons:
            outputs.append(neuron.calculate_output(inputs))
        return outputs

    def get_outputs(self):
        outputs = []
        for neuron in self.neurons:
            outputs.append(neuron.output)
        return outputs

class Neuron:
    def __init__(self, bias):
        self.bias = bias
        self.weights = []

    def calculate_output(self, inputs):
        self.inputs = inputs
        self.output = self.squash(self.calculate_total_net_input())
        return self.output

    def calculate_total_net_input(self):
        total = 0
        for i in range(len(self.inputs)):
            total += self.inputs[i] * self.weights[i]
        return total + self.bias

    # 激活函数sigmoid
    def squash(self, total_net_input):
        return 1 / (1 + math.exp(-total_net_input))


    def calculate_pd_error_wrt_total_net_input(self, target_output):
        return self.calculate_pd_error_wrt_output(target_output) * self.calculate_pd_total_net_input_wrt_input();

    # 每一个神经元的误差是由平方差公式计算的
    def calculate_error(self, target_output):
        return 0.5 * (target_output - self.output) ** 2


    def calculate_pd_error_wrt_output(self, target_output):
        return -(target_output - self.output)


    def calculate_pd_total_net_input_wrt_input(self):
        return self.output * (1 - self.output)


    def calculate_pd_total_net_input_wrt_weight(self, index):
        return self.inputs[index]


# 文中的例子:

nn = NeuralNetwork(2, 2, 2, hidden_layer_weights=[0.15, 0.2, 0.25, 0.3], hidden_layer_bias=0.35, output_layer_weights=[0.4, 0.45, 0.5, 0.55], output_layer_bias=0.6)
for i in range(10000):
    nn.train([0.05, 0.1], [0.01, 0.09])
    print(i, round(nn.calculate_total_error([[[0.05, 0.1], [0.01, 0.09]]]), 9))


#另外一个例子,可以把上面的例子注释掉再运行一下:

# training_sets = [
#     [[0, 0], [0]],
#     [[0, 1], [1]],
#     [[1, 0], [1]],
#     [[1, 1], [0]]
# ]

# nn = NeuralNetwork(len(training_sets[0][0]), 5, len(training_sets[0][1]))
# for i in range(10000):
#     training_inputs, training_outputs = random.choice(training_sets)
#     nn.train(training_inputs, training_outputs)
#     print(i, nn.calculate_total_error(training_sets))

3. Red de mapas autoorganizados SOM

La red SOM, también conocida como red Kohonen , es un tipo de red neuronal competitiva autoorganizada.Esta red es una red de aprendizaje no supervisada , que puede identificar características ambientales y agruparlas automáticamente. Se autoorganiza y cambia de forma autoadaptativa los parámetros y la estructura de la red al buscar automáticamente las leyes internas y las propiedades esenciales en las muestras.

1. Topología de red SOM

  • La red consta de dos capas, una capa de entrada y una capa de salida. La capa de salida también se denomina capa de competencia , excluyendo la capa oculta.
  • Cada nodo de entrada en la capa de entrada está completamente conectado a un nodo de salida.
  • Los nodos de salida se distribuyen en una estructura bidimensional y existen conexiones laterales entre los nodos. Por lo tanto, para un determinado nodo de salida, existirá un determinado número de "celdas adyacentes", es decir, nodos adyacentes, dentro de un determinado rango de vecindad .

2. Principio

  1. Similitud entre la clasificación y los patrones de entrada : la clasificación consiste en asignar los patrones de entrada que se reconocerán a sus respectivas clases de patrones bajo la guía de señales de mentor, como el conocimiento de categoría. La clasificación sin guía de mentor se denomina agrupación. El propósito de la agrupación es Muestras de patrones similares se clasifican en una clase, y las diferentes se separan para lograr la similitud dentro de la clase y la separación entre clases de muestras de patrones . Dado que las muestras de entrenamiento para el aprendizaje no supervisado no contienen la salida deseada, no hay conocimiento previo de a qué clase debe pertenecer una determinada muestra de patrón de entrada. Para un conjunto de patrones de entrada, solo se pueden dividir en varias categorías según el grado de similitud entre ellos. Por lo tanto, la similitud es la base para agrupar los patrones de entrada .
  2. Medida de similitud : La medida de similitud del vector patrón de entrada de la red neuronal se puede medir por la distancia entre los vectores . Los métodos más utilizados son el método de la distancia euclidiana y el método del coseno.
  3. Principio de aprendizaje competitivo: la base fisiológica de las reglas de aprendizaje competitivo es la inhibición lateral de las células nerviosas: cuando se excita una célula nerviosa, inhibirá las células nerviosas circundantes. El desincentivo más fuerte es el solipsismo de competir para ganar, una práctica conocida como Winner-Take-All. Las reglas de aprendizaje competitivo se obtienen del fenómeno de inhibición lateral de las células nerviosas. Sus pasos de aprendizaje son: A. Normalización de vectores, B. Búsqueda de neuronas ganadoras, C. Salida de red y ajuste de peso, D. Procesamiento de renormalización .
Estrategia de ajuste de competencia de la red SOM, actualización de nodos ganadores y nodos adyacentes

3. Algoritmo específico

Durante el proceso de aprendizaje de la red, cuando se ingresa una muestra en la red, las neuronas en la capa de competencia calculan la distancia euclidiana entre la muestra de entrada y el peso de las neuronas en la capa de competencia, y la neurona con la distancia más pequeña es la ganadora. neurona. Ajuste los pesos de la neurona ganadora y las neuronas adyacentes para que los pesos de la neurona ganadora y sus alrededores estén cerca de la muestra de entrada. A través del entrenamiento repetido, los pesos finales de conexión de cada neurona tienen una cierta distribución. Esta distribución organiza la similitud entre los datos en neuronas que representan varios tipos, de modo que las neuronas del mismo tipo tienen coeficientes de peso similares y las neuronas de diferentes tipos. La diferencia en el coeficiente de peso es obvio. Cabe señalar que durante el proceso de aprendizaje, el valor del peso modifica la velocidad de aprendizaje y el campo de neuronas disminuye constantemente, de modo que se concentran gradualmente el mismo tipo de neuronas.

En general, la red SOM incluye tres partes: inicialización, competencia e iteración .

3.1 Inicialización

  1. Normalice el conjunto de datos.
  2. Diseñar la estructura de capas competitiva. Generalmente, se establece de acuerdo con el número estimado de clústeres. Por ejemplo, para dividir en 4 categorías, puedo diseñar la capa de competencia como 2*2, 1*4, 4*1. La red SOM no necesita proporcionar el número de clústeres por adelantado, y la red identifica automáticamente los datos de la categoría.
  3. Preestablece el nodo de peso de las neuronas de la capa competitiva. Generalmente, estableceremos los nodos de peso de acuerdo con las dimensiones de los datos de entrada y el número de categorías estimado por la capa de competencia.Si la entrada son datos bidimensionales y el agrupamiento final es de 4 categorías, la matriz de peso es 2 *4.
  4. Inicialice el radio de la vecindad y la tasa de aprendizaje .

3.2 Proceso de competencia

  1. Seleccione muestras aleatoriamente y calcule la distancia a cada nodo de neurona en la capa de competencia de acuerdo con los nodos de peso . Generalmente, elegimos la fórmula de la distancia euclidiana para calcular la distancia.

    (U otro método: el producto escalar del peso w y la entrada x es el más grande para determinar el nodo ganador)
  2. Seleccione el nodo ganador. A través de la competencia, el nodo de la neurona con la menor distancia se selecciona como el nodo ganador.

Mejor unidad de coincidencia (BMU) 优胜节点: el nodo ganador (o peso) que tiene la distancia más corta desde sí mismo hasta el vector de muestra. Existen numerosas formas de determinar la distancia, sin embargo, el método más utilizado es la Distancia Euclidiana, que  es la métrica más común para calcular la distancia entre los pesos y el vector de muestra.

3.3 Proceso iterativo

1. Delimite todos los nodos en la vecindad ganadora. Según el radio de la vecindad, los nodos cuya distancia es menor que el radio de la vecindad se delimitan para formar la vecindad ganadora.

2. Actualice iterativamente los pesos de los nodos en el vecindario ganador. La idea de la actualización es que cuanto más cerca del nodo ganador, mayor será el rango de actualización; cuanto más lejos del nodo ganador, menor será el rango de actualización, por lo que debemos agregar una restricción de actualización a diferentes nodos, que generalmente puede obtenerse mediante una función gaussiana. Además, nuestro propósito de actualizar los nodos es acercar el nodo ganador al punto de muestra, por lo que usamos la siguiente fórmula para actualizar el peso del nodo:

Diferentes funciones de vecindad:

  • case 'burbuja', H = (Ud<=radius(t));
  • caso 'gaussiano', H = exp(-Ud/(2*radius(t)));
  • case 'cutgauss', H = exp(-Ud/(2 radio(t))) .  (Ud<=radio(t));
  • case 'ep', H = (1-Ud/radio(t)) .* (Ud<=radio(t));

3.4 función de decaimiento función de decaimiento

Otra característica de la red SOM es que la tasa de aprendizaje y el rango de vecindario decaerán gradualmente con el número de iteraciones.

La función de decaimiento más utilizada es 1 / (1 + t/T)

sigma(t) = sigma / (1 + t/T)

tasa_de_aprendizaje(t) = tasa_de_aprendizaje / (1 + t/T)

t representa el número actual de iteraciones
T = número total de iteraciones / 2

4. Ejemplos prácticos

Los pesos iniciales son w1 = (0.45,0.89) , w2 = (0.55,0.83) , w3 = (0.95,0.32) y w4 = (0.62,0.78) . Deje que las cuatro neuronas (N = 4), N1, N2, N3 y N4 estén ubicadas en la cuadrícula de las posiciones del plano de coordenadas cartesianas (0,0), (1,0), (1,0) y (1,1) respectivamente medio. Suponga que la red acepta un vector de entrada bidimensional (A=2) (x1,x2). Sea w_i= (w1_i,w2_i) el peso de la neurona i.radio de vecindad = 0.6. Vector de entrada X=(3,1). Suponga que la tasa de aprendizaje = 0,5 y es constante en todo momento. Suponga pesos iniciales como se describe en la figura.

5. Código

Referencias

Introducción básica cero al aprendizaje profundo (1) - Perceptron - Homework Tribe Cmd Markdown Editor Reader

Un artículo para comprender el método de retropropagación en la red neuronal - BackPropagation

retropropagación

Resumen de los algoritmos de redes neuronales_Qingqing_Amanda's Blog-CSDN Blog

Descenso de gradiente por lotes, descenso de gradiente estocástico, descripción de comparación de descenso de gradiente estocástico de mini lotes: - Saber

Derivación de algoritmos de aprendizaje automático e implementación de escritura a mano 07——SOM Network-Knowledge

Red neuronal de mapeo autoorganizado (SOM)_Idea Reply Blog-CSDN Blog

https://medium.com/analytics-vidhya/how-does-self-organizing-algorithm-works-f0664af9bf04

Supongo que te gusta

Origin blog.csdn.net/m0_64768308/article/details/129773449
Recomendado
Clasificación