Эта статья о переносе сути Карпарти минимальный персонаж rnn на Swift. Суть в том, что это очень простой rnn, который обучен предсказывать следующего персонажа, используя сборник романов о Шерлоке Холмсе. У Карпаты есть поддерживающий пост в блоге о сути и многом другом.

Модель состоит из одного скрытого слоя с простым модулем rnn, использующим tanh для сжатия ввода нового символа с предыдущим скрытым состоянием. Потеря начинается со 114 и сходится к 52. Я отключил генерацию текста, потому что сеть и генератор текста слишком просты, чтобы текст имел смысл. Сгенерированный текст блога i karpathy основан на модели со сложенными слоями lstm-нейронов и лучевым поиском для генерации текста.

Простота моделей и отсутствие абстракций облегчают понимание основных концепций языковой модели и делают ее хорошей отправной точкой для экспериментов со Swift. Вы можете найти код на github simple_rnn_swift с инструкцией по установке и запуску с помощью xcode.

Есть две версии с минимальными изменениями исходного кода.

simple_rrn_python.swift использует бесшовную интеграцию с python, предоставляемую набором инструментов swift4tensorflow. Основные изменения:

Строка: «import Python» импортирует установку Python из системной среды, игнорируя ваше возможное использование conda. Библиотеки импортируются, объявляя их как переменные: пусть np = Python.import («numpy»).

Я добавляю две строки, чтобы использовать библиотеки Python, которые у меня были в conda:

  • пусть sys = Python.import («система»)
  • sys.path.append("/anaconda3/lib/python3.6/сайт-пакеты")

Переменные должны быть объявлены как let (константа) или var. Большинство типов являются производными от компилятора.

Когда компилятор не уверен, что значение допустимо, он просит разработчика развернуть его с помощью оператора ! или ? . В приведенном ниже фрагменте xs — это словарь, в который мы вставляем массивы numpy (быстрого типа PythonObject). При использовании ранее вставленного массива мы должны развернуть xs[t] с помощью !, как в np.dot(Wxh, xs[t]!), чтобы сигнализировать о том, что мы уверены значение не равно нулю.

var xs = [Int: PythonObject]()

для t в 0..‹3 {

xs[t] = np.zeros([vocab_size,1])

пусть w = np.dot (Wxh, xs [t]!)

}

Развертывание должно быть сведено к минимуму при написании нового кода.

Существует проблема со сборкой мусора временной переменной при использовании XCode. Каждая итерация выделяет много временных переменных Python fx. np.dot(Wxh, xs[t]!) . Проблема в том, что они не освобождаются автоматически, поэтому для обучения в конечном итоге заканчивается память. Я читал, что Swift берет на себя ответственность за объекты Python. Это может объяснить, почему вызов gc.collet() не помогает.

Мой вывод состоит в том, что интеграцию с Python лучше всего использовать для нечастых операций ввода-вывода, таких как загрузка данных и построение графиков с использованием matplotlib.

simple_rrn_swift использует tensorflow в качестве библиотеки матриц вместо numpy. Код очень похож на simple_rrn_python — на первый взгляд. Большая разница заключается в использовании ссылок на объекты numpy по сравнению с объектом тензорного значения:

  • вар bh_python = np.zeros([hidden_size, 1])
  • var bh_swift = Tensor‹Float›( нули: [hidden_size, 1 ] )

bh_python ссылается на справочное значение массива numpy, тогда как bh_swift является значением. Поэтому следующее не действует в simple_rrn_swift:

//обновляем веса и смещения

var params = [Wxh, Whh, Why, bh, by]

var dparams = [dWxh, dWhh, dWhy, dbh, dby]

var mems = [mWxh, mWhh, mWhy, mbh, mby]

для i в 0..‹params.count {

mems[i] += dparams[i] * dparams[i]

params[i] += -learning_rate * dparams[i] / np.sqrt(mems[i] + 1e-8) }

Код компилируется в обеих версиях, и потери уменьшаются с каждой итерацией в simple_rrn_python, но ничего не происходит в simple_rrn_swift? . Причина в том, что копия значения объекта ссылается на массив numpy по-прежнему ссылается на тот же массив numpy. Принимая во внимание, что копия значения объекта значения тензора в var params = [Wxh, Whh, Why, bh, by] создает новое выделение памяти с копиями тензоров. Таким образом, обновление параметров не влияет на Wxh и т. д.

Простое исправление состояло в том, чтобы обновить Wxh напрямую:

мВтхВ += dВхВ * dВхВ

Wxh += -learning_rate * dWxh / sqrt (mWxh + 1e-8)

Следующая цель на пути к Swift — найти больше способов использовать тензоры из tensorflow для разработки новых алгоритмов.