Если бы я показал вам случайное английское имя, я уверен, вы смогли бы точно сказать, принадлежит оно мужчине или женщине. Как ты это делаешь? Вы сознательно ищите в названии какие-то конкретные закономерности? Я так не думаю.

Недавно мне пришлось написать код на стороне клиента, который может автоматически заполнять поле пола в регистрационной форме - с умеренной точностью. Сначала я подумал, что буду искать закономерности в последних трех или четырех символах имени. Например, имена, оканчивающиеся на -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.