Вопрос или проблема
В настоящее время я работаю над реализацией свёрточного слоя на Python для модели обработки естественного языка. Однако я столкнулся с проблемой в свёрточном слое, которую не могу решить.
Проблема состоит из двух частей:
-
Получение всех нулей на выходе: Когда я запускаю прямой проход моего свёрточного слоя, я постоянно получаю все нули на выходе на каждой позиции. Я проверил это, распечатав значения во время выполнения. Это неожиданно, поскольку я правильно инициализировал массив выходных данных.
-
Завершение на определённой итерации: Кроме того, прямой проход завершается внезапно на определённой итерации, а именно на 30-й итерации. Я не могу понять, почему цикл выходит преждевременно.
Вот упрощённая версия моего класса Conv1DLayer:
import numpy as np
class Conv1DLayer:
def __init__(self, num_filters, filter_size):
self.num_filters = num_filters
self.filter_size = filter_size
self.conv_filter = np.random.randn(filter_size, 1)
def loss(self, pred, target):
# вычислить функцию потерь
return np.mean((pred - target) ** 2)
def forward(self, inputs):
self.inputs = inputs
num_inputs = inputs.shape[1]
output_length = num_inputs - self.filter_size + 1
self.output = np.zeros((self.num_filters, output_length))
# Свёртка
# размер входа в основном соответствует размеру словаря
print("размер входа: ", inputs.shape)
print("количество входов: ", num_inputs)
print("размер фильтра: ", self.conv_filter.shape)
print("размер фильтра: ", self.filter_size)
print("размер выходных данных: ", self.output.shape)
print("длина выходных данных: ", output_length)
for i in range(output_length):
if i+self.filter_size > num_inputs:
break
receptive_field = inputs[i:i+self.filter_size, 1].toarray()
print("размер рецептивного поля: ", receptive_field.shape)
self.output[:, i] = np.dot(receptive_field.T, self.conv_filter)
print("выход на " + str(i) + str(self.output[:, i]))
self.output[:, i] = np.maximum(0, self.output[:, i])
return self.output
def backward(self, grad_outputs, learning_rate):
grad_input = np.zeros(grad_outputs.shape)
grad_filter = np.zeros(self.conv_filter.shape)
for i in range(grad_outputs.shape[0]):
for j in range(self.num_filters):
receptive_field = self.inputs[i:i+self.filter_size]
grad_input[i:i+self.filter_size] += self.conv_filter[:, j] * grad_outputs[i, j]
grad_filter[:, j] += receptive_field * grad_outputs[i, j]
# Обновление весов
self.conv_filter -= learning_rate * grad_filter
return grad_input
Я пробовал различные изменения в коде, включая проверку формы входных массивов, корректировку рецептивного поля и обновление условия цикла, но не смог решить проблему.
Вот мой вывод
$ python model.py
размер входа: (32, 2010)
количество входов: 2010
размер фильтра: (3, 1)
размер фильтра: 3
размер выходных данных: (10, 2008)
длина выходных данных: 2008
размер рецептивного поля: (3, 1)
выход на 0[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 1[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 2[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 3[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 4[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 5[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 6[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 7[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 8[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 9[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 10[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 11[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 12[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 13[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 14[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 15[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 16[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 17[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 18[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 19[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 20[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 21[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 22[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 23[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 24[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 25[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 26[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 27[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 28[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (3, 1)
выход на 29[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
размер рецептивного поля: (2, 1)
Трассировка (последний вызов был последним):
Файл "C:\Users\maste_0c98yk4\OneDrive\Desktop\Projects\Natural Language Processing Model - Sentiment Analysis\model.py", строка 89, в <module>
model.train(X, labels, num_epochs=10, batch_size=32)
Файл "C:\Users\maste_0c98yk4\OneDrive\Desktop\Projects\Natural Language Processing Model - Sentiment Analysis\model.py", строка 44, в train
conv_output = self.conv_layer.forward(inputs)
Файл "C:\Users\maste_0c98yk4\OneDrive\Desktop\Projects\Natural Language Processing Model - Sentiment Analysis\convolution.py", строка 32, в forward
self.output[:, i] = np.dot(receptive_field.T, self.conv_filter)
ValueError: формы (1,2) и (3,1) не согласованы: 2 (разм. 1) != 3 (разм. 0)
(tf)
Ожидаемое поведение:
- Прямой проход должен правильно вычислять свёртку, производя ненулевые значения на выходе.
- Цикл должен итерироваться по всем допустимым позициям, не завершаясь преждевременно.
Моя оценка
Я считаю, что проблема, скорее всего, заключается в том, как я использую индексы, но ни chatgpt, ни bard не смогли её исправить, поэтому это может быть что-то глубже.
Любая помощь будет очень ценна. Спасибо
Для второй проблемы:
Размер ваших входных данных (32,2010).
Здесь вы выполняете цикл for (выходная длина) количество раз, то есть 2008. В рецептивном поле вы создаете ndarray размерности (3, 1) из первого индекса (по сути, нарезая 1-й столбец) входов (которые имеют длину 32), поэтому на 31-й итерации размер рецептивного поля составляет (2,1).
Поэтому, во время операции произведения точки ваши две матрицы – это receptive_field.T(1,2) и self.conv_filter(3,1). Эти матрицы не могут быть перемножены, именно поэтому вы получаете ошибку значения, что формы не согласованы.
Редактировать:
Смотрите этот код для прямого прохода. Я учёл только 1 фильтр в этом случае. Вы можете изменить его для большего количества фильтров (объединив выходы свёртки для разных фильтров по оси z). Также я предположил, что вы хотите выполнить свёртку вдоль оси x.
В свёртке мы скользим фильтром по входным данным, и значение свёртки основано на окне вокруг xt согласно:
На практике a изменяется от 0 до размера фильтра.
import numpy as np
class Conv1DLayer:
def __init__(self, num_filters, filter_size):
self.num_filters = num_filters
self.filter_size = filter_size
self.conv_filter = np.random.randn(filter_size, 1)
print(self.conv_filter)
def loss(self, pred, target):
# вычислить функцию потерь
return np.mean((pred - target) ** 2)
def forward(self, inputs):
self.inputs = inputs
rows = inputs.shape[0]
columns = inputs.shape[1]
output_length = columns - self.filter_size + 1
self.output = np.zeros((rows, output_length))
# Свёртка
# размер входа в основном соответствует размеру словаря
print("размер входа: ", inputs.shape)
print("количество входов: ", rows)
print("размер фильтра: ", self.conv_filter.shape)
print("размер фильтра: ", self.filter_size)
print("размер выходных данных: ", self.output.shape)
print("длина выходных данных: ", output_length)
j=0
while (j < rows):
i = 0
while (i+self.filter_size <= columns):
receptive_field = inputs[j:j+1, i:i+self.filter_size]
print("размер рецептивного поля: ", receptive_field.shape)
self.output[j, i] = np.dot(receptive_field, self.conv_filter)
print("выход на " + str(j)+" " +str(i) + " "+ str(self.output[j, i]))
self.output[j, i] = np.maximum(0, self.output[j, i])
i=i+1
j= j+1
return self.output
Вы можете ознакомиться с этой статьёй для более ясного представления о 1d свёртке.
Надеюсь, это поможет!
Ответ или решение
Проблема с реализацией свёрточного слоя в Python может быть достаточно сложной, особенно когда возникает ситуация, когда все значения выходного массива равны нулю, и выполнение программы прерывается на определенной итерации. Давайте разберем вашу ситуацию, выявим возможные причины и предложим решения.
1. Проблема с выводом (все значения ноль)
Возможные причины:
-
Некорректное приготовление входных данных: Убедитесь, что входные данные (inputs) корректно подготавливаются и содержат ожидаемые значения. Если вы используете разреженные векторы (например,
scipy.sparse
), которые необходимо конвертировать в плотные массивы, необходимо это сделать перед выполнением свертки. -
Неверная индексация: Ваша реализация свёрточного слоя может содержать ошибки в индексации, что может приводить к тому, что вы получаете нулевые значения. Когда вы берете
receptive_field
, убедитесь, что вы ссылаетесь на правильные индексы. -
Проблемы с сверточным фильтром: Если
conv_filter
инициализируется значениями, которые близки к нулю (например, если использование небольших значений случайной инициализации приводит к тому, что итоговые значения нулевые), это также может приводить к нулевым выходным значениям.
Что делать:
- Проверьте входные данные, используя вывод значений перед выполнением свёртки.
- Подумайте об инициализации свёрточного фильтра с помощью более масштабных значений или использования другого метода, например, инициализация по методу Глорта, чтобы избежать попадания в состояние "глубокого нуля".
2. Преждевременное завершение на определенной итерации
Возможные причины:
-
Прерывание цикла: Ваша реализация использует условие
i + self.filter_size > num_inputs
в цикле. Это условие неправильно для вашей логики, так как то, что вы на самом деле хотите проверить, это то, что индексi
находится в допустимых пределах. -
Неверная работа с размерностью элементов: Когда ваш
receptive_field
запрашивает элементы слишком близкие к последним позициям входных данных, это может привести к ошибке, связанной с несовпадением форматов.
Что делать:
- Измените условие выхода из цикла. Вместо использования разбиения индекса попробуйте другой вариант, чтобы обеспечить корректность индексации и не допустить выхода за границы массива.
- Обратите внимание на то, как создается
receptive_field
, и убедитесь, что размерности корректно соответствуют фильтрам и входным данным.
Исправленная версия вашего кодa
Вот пример того, как можно переписать метод forward
, чтобы избежать описанных проблем:
import numpy as np
class Conv1DLayer:
def __init__(self, num_filters, filter_size):
self.num_filters = num_filters
self.filter_size = filter_size
self.conv_filter = np.random.randn(filter_size, 1)
def forward(self, inputs):
self.inputs = inputs
num_inputs = inputs.shape[1]
output_length = num_inputs - self.filter_size + 1
self.output = np.zeros((self.num_filters, output_length))
for i in range(output_length):
receptive_field = inputs[:, i:i + self.filter_size]
self.output[:, i] = np.dot(receptive_field, self.conv_filter)
self.output[:, i] = np.maximum(0, self.output[:, i]) # Применение функции активации
return self.output
Заключение
Мощность свёрточных нейронных сетей может быть использована максимально эффективно при правильной реализации и отладке. Следование описанным рекомендациям и внимательное отношение к структурированию входных данных, инициализации параметров и управлению границами массивов поможет устранить возникшие проблемы. Убедитесь, что вы проверяете выводы на каждой стадии выполнения кода, чтобы своевременно выявлять ошибки.
Если эти советы и исправления не помогут решить ваши проблемы, рассмотрите возможность использования отладчика, чтобы тщательно проанализировать переменные и ходы выполнения программы. Удачи вам в ваших разработках!