Если бы я показал вам случайное английское имя, я уверен, вы смогли бы точно сказать, принадлежит оно мужчине или женщине. Как ты это делаешь? Вы сознательно ищите в названии какие-то конкретные закономерности? Я так не думаю.
Недавно мне пришлось написать код на стороне клиента, который может автоматически заполнять поле пола в регистрационной форме - с умеренной точностью. Сначала я подумал, что буду искать закономерности в последних трех или четырех символах имени. Например, имена, оканчивающиеся на -ine или -lla, почти всегда женские, а имена, заканчивающиеся на -iam или -us почти всегда мужчины. Однако таких концовок слишком много, и бывают исключения. Кроме того, я не хотел тратить свое время на изучение закономерностей в именах.
Поэтому я решил создать искусственную нейронную сеть, которая сможет это сделать. И угадайте, что, используя библиотеку под названием Synaptic, я смог создать ее менее чем за полчаса. Что ж, у него точность ›80% (для подтверждения концепции это удовлетворительно). В этом посте я покажу вам, как мне это удалось.
Первым делом я добавил Synaptic в раздел head HTML-страницы. Поскольку Synaptic доступен на CDNJS, мне даже не пришлось его загружать.
<script src="https://cdnjs.cloudflare.com/ajax/libs/synaptic/1.0.8/synaptic.min.js"></script>
Теперь мне нужно было решить, сколько слоев должно быть в нейронной сети. Мне также нужно было решить, сколько нейронов должно присутствовать в каждом слое. После нескольких минут проб и ошибок я пришел к следующей конфигурации:
- 7 нейронов для входного слоя. Они будут представлять последние 7 символов имени.
- 6 нейронов для скрытого слоя
- 2 нейрона для выходного слоя (один для мужчины, один для женщины)
var INPUT_LENGTH = 7; var myNetwork = new Synaptic.Architect.Perceptron(INPUT_LENGTH,6,2);
Теперь мне нужна была функция, которая может преобразовывать имя в формат, который понимает нейронная сеть - она может принимать только числа с плавающей запятой от 0 до 1. Итак, я решил преобразовать каждый из последних 7 символов имени в их символ. коды и делите их на 1000. Если имя было слишком коротким, я дополнял его пробелами.
function convertNameToInput(name) { name = name.toLowerCase(); if(name.length > INPUT_LENGTH) name = name.substring(INPUT_LENGTH); while(name.length < INPUT_LENGTH) name = " " + name; var characters = name.split(""); return characters.map( (c) => c == " " ? 0 : c.charCodeAt(0)/1000 ); }
Пора получить данные для обучения. К счастью, Марк Кантровиц создал большие списки мужских и женских имен.
Когда у меня были имена в двух массивах JavaScript, мне пришлось преобразовать их в формат, понятный Trainer Synaptic. Это простой формат. Вы просто указываете значения входных нейронов и значения выходных нейронов. Два цикла for сделали свою работу.
var trainingData = []; var trainer = new synaptic.Trainer(myNetwork); for(var i = 0; i < females.length; i++) { trainingData.push({ input: convertNameToInput(females[i]), output: [0, 1] // Male = false, Female = true }); } for(var i = 0; i < males.length; i++) { for(var j = 0; j < 2; j++) { trainingData.push({ input: convertNameToInput(males[i]), output: [1, 0] // Male = true, Female = false }); } }
Обучающие данные считаются хорошими только в том случае, если они хорошо перемешаны. Я использовал Underscore.js для перетасовки.
for(var i=0;i<10;i++) trainingData = _.shuffle(trainingData);
Все, что оставалось сделать, - это провести собственно тренировку. Всего я выполнил 5000 итераций: 25 пакетов по 200 итераций в каждой. Что касается скорости обучения, я экспериментировал с различными значениями и обнаружил, что 0,01 было оптимальным. Что касается функции стоимости, я попробовал и CROSS_ENTROPY, и MSE и обнаружил, что MSE лучше.
for(var i = 0 ; i < 25 ; i++) { trainer.train(trainingData, { rate: 0.01, iterations: 200, shuffle: true, cost: synaptic.Trainer.cost.MSE }); var error = trainer.test(trainingData)["error"]; console.log( "Iteration " + ((i+1) * 200) + " --> Error: " + error ); }
И, наконец, функция, которая может фактически принимать имя в качестве входных данных и сообщать пол. Это просто сравнило значения двух выходных нейронов и использовало самое высокое значение.
function getGender(name) { var result = myNetwork.activate(convertNameToInput(name)); if(result[0] > result[1]) return "Male (" + (result[0]*100).toFixed() + "% sure)"; return "Female (" + (result[1]*100).toFixed() + "% sure)"; }
Все готово. Вот что у меня получилось после тренировки.
На мой взгляд, 82% - это приемлемая точность. Может быть, с большим количеством итераций я мог бы достичь чего-то немного большего.
Наконец, пришло время проверить это с несколькими случайными именами.
Неплохо. Вы могли бы возразить, что есть отличные библиотеки, которые более точны, но эй, нейронная сеть, работающая в браузере, определенно круто!
На этом этапе я мог просто экспортировать нейронную сеть в виде строки JSON и использовать ее на любой странице HTML, которую я хотел.
Если вы нашли эту статью полезной, нажмите на это сердце, чтобы ее прочитало больше людей!
Более подробные статьи о машинном обучении и Synaptic.js вы можете прочитать в моем блоге Progur.