Skip to content

Instantly share code, notes, and snippets.

@obenjiro
Last active October 23, 2020 17:19
Show Gist options
  • Save obenjiro/20525f6d0193eb0786fe98902b3c3adb to your computer and use it in GitHub Desktop.
Save obenjiro/20525f6d0193eb0786fe98902b3c3adb to your computer and use it in GitHub Desktop.
CoLabJS: преобразуем градусы Цельсия в градусы Фаренгейта.ipynb
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "CoLabJS: преобразуем градусы Цельсия в градусы Фаренгейта.ipynb",
"provenance": [],
"toc_visible": true,
"include_colab_link": true
},
"kernelspec": {
"name": "javascript",
"display_name": "Javascript"
},
"accelerator": "GPU"
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/obenjiro/20525f6d0193eb0786fe98902b3c3adb/celcius2farenheit.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "fsqcND6Klagn",
"colab_type": "text"
},
"source": [
"# Подготавливаем JS для работы с Google Colab"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "bjNF3VjPjJHV",
"colab_type": "text"
},
"source": [
"Выполните команды указанные внизу и нажмите F5 (перезагрузите страницу) сразу после того как их выполение закончиится."
]
},
{
"cell_type": "code",
"metadata": {
"id": "ZVkF16NwcMsZ",
"colab_type": "code",
"colab": {}
},
"source": [
"!npm install -g npm@latest\n",
"!npm cache verify\n",
"!npm install -g --unsafe-perm ijavascript\n",
"!ijsinstall --install=global\n",
"!jupyter-kernelspec list"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "r2UzIt5Gi6Em",
"colab_type": "text"
},
"source": [
"Вот и все! :) Теперь вы можете писать на JavaScript в Google Colab"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "MMyXtMptltc9",
"colab_type": "text"
},
"source": [
"#Базовые настройки"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "qbVfsd0emCWY",
"colab_type": "text"
},
"source": [
"**Не используйте `const` or `let`! Используйте вместо этого `var`**\n",
"\n",
"Добавляем пару helpers для выполнение команд в терминале и запуска асинхронного кода:"
]
},
{
"cell_type": "code",
"metadata": {
"id": "skqAs_UHc8_P",
"colab_type": "code",
"colab": {}
},
"source": [
"var { spawn } = require('child_process');\n",
"var sh = (cmd) => { \n",
" $$.async();\n",
" var sp = spawn(cmd, { cwd: process.cwd(), stdio: 'pipe', shell: true, encoding: 'utf-8' });\n",
" sp.stdout.on('data', data => console.log(data.toString()));\n",
" sp.stderr.on('data', data => console.error(data.toString()));\n",
" sp.on('close', () => $$.done());\n",
"};\n",
"var run_async = async (pf) => {\n",
" $$.async();\n",
" await pf();\n",
" $$.done();\n",
"};\n",
"sh('npm init -y');"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "1oqifUNtdID-",
"colab_type": "code",
"colab": {}
},
"source": [
"sh('node -v; npm -v');"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "28tLRTVq8xms",
"colab_type": "code",
"colab": {}
},
"source": [
"sh('npm install @tensorflow/tfjs-node-gpu')"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "psJPTwp79NXv",
"colab_type": "code",
"colab": {}
},
"source": [
"var tf = require('@tensorflow/tfjs-node-gpu');\n",
"console.log(tf.backend().isUsingGpuDevice);"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "F-aLZ4Vk4FAq",
"colab_type": "text"
},
"source": [
"#Подготовим данные для тренировки\n",
"\n",
"(данный playbook просто скопирован с https://colab.research.google.com/drive/1Bw-WlZeTV-D9JPraPk1-QjERqC_c-C9l и являеться лишь переводом на JavaScript)",
"\n",
"Как мы уже видели ранее, методика машинного обучения с учителем основывается на поиске алгоритма преобразования входных данных в выходные. Так как задачей этого CoLab является создание модели, которая может выдать результат преобразования градусов по Фаренгейту в градусы Цельсия, создадим два списка - celsius_q и fahrenheit_a, которые мы используем при обучении нашей модели.\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "GcQiKW6sJFZX",
"colab_type": "code",
"outputId": "6ceed915-0404-4c34-e5e4-1bdf34436eb7",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 136
}
},
"source": [
"var celsius_q = tf.tensor([-40, -10, 0, 8, 15, 22, 38]);\n",
"var fahrenheit_a = tf.tensor([-40, 14, 32, 46, 59, 72, 100]);\n",
"\n",
"\n",
"for (var i = 0; i < celsius_q.shape[0]; i++) {\n",
" console.log(celsius_q.dataSync()[i] + ' градусов Цельсия = ' + celsius_q.dataSync()[i] + ' градусов Фаренгейта')\n",
"}"
],
"execution_count": 13,
"outputs": [
{
"output_type": "stream",
"text": [
"-40 градусов Цельсия = -40 градусов Фаренгейта\n",
"-10 градусов Цельсия = -10 градусов Фаренгейта\n",
"0 градусов Цельсия = 0 градусов Фаренгейта\n",
"8 градусов Цельсия = 8 градусов Фаренгейта\n",
"15 градусов Цельсия = 15 градусов Фаренгейта\n",
"22 градусов Цельсия = 22 градусов Фаренгейта\n",
"38 градусов Цельсия = 38 градусов Фаренгейта\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "5oP1j6ivZQKA",
"colab_type": "text"
},
"source": [
"**Некоторая терминология машинного обучения**\n",
"\n",
"* **Свойство (признак)** - входное(ые) значение нашей модели. В данном случае единичное значение - градусы Цельсия.\n",
"* **Метки** - выходные значения, которые наша модель предсказывает. В данном случае единичное значение - градусы Фаренгейта.\n",
"* **Пример** - пара входных-выходных значений используемых для тренировки. В данном случае это пара значений из `celsius_q` и `fahrenheit_a` под определённым индексом, например, `(22,72)`."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "dE-fVITIZabr",
"colab_type": "text"
},
"source": [
"# Создаём модель"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "bTtTRx_jZa_P",
"colab_type": "text"
},
"source": [
"Далее мы создаём модель. Мы будем использовать максимально упрощенную модель - модель плотной сети (`Dense`-сеть).\n",
"Так как задача достаточно незамысловата, то и сеть будет состоять из единственного слоя с единственным нейроном. "
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "-DAZbC91ZfeW",
"colab_type": "text"
},
"source": [
"**Строим сеть**\n",
"\n",
"Мы назовём слой `l0` (**l**ayer и ноль) и создадим его, инициализировав `tf.keras.layers.Dense` со следующими параметрами:\n",
"\n",
"* `input_shape=[1]` - этот параметр определяет размерность входного параметра - единичное значение. Массив размером $ 1 \\times 1 $ с единственным значением. Так как это первый (и единственный) слой, то и размерность входных данных соответствует размерности всей модели. Единственное значение - значение с плавающей запятой, представляющее градусы Цельсия.\n",
"\n",
"* `units=1` - этот параметр определяет количество нейронов в слое. Количество нейронов определяет то, как много внутренних переменных слоя будет использовано для обучения при поиске решения поставленной задачи. Так как это последний слой, то его размерность равна размерности результата - выходного значения модели - единственного числа с плавающей запятой представляющего собой градусы Фаренгейта. (В многослойной сети размеры и форма слоя `input_shape` должны соответствовать размерам и формам следующего слоя)."
]
},
{
"cell_type": "code",
"metadata": {
"id": "rz1cDsTbZk_a",
"colab_type": "code",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 612
},
"outputId": "491657d9-b36d-4028-94f3-3aea82f64fce"
},
"source": [
"var l0 = tf.layers.dense({ units: 1, inputShape: [1] });\n",
"console.log(l0)"
],
"execution_count": 15,
"outputs": [
{
"output_type": "stream",
"text": [
"Dense {\n",
" _callHook: null,\n",
" _addedWeightNames: [],\n",
" _stateful: false,\n",
" id: 0,\n",
" activityRegularizer: null,\n",
" inputSpec: [ { minNDim: 2 } ],\n",
" supportsMasking: true,\n",
" _trainableWeights: [],\n",
" _nonTrainableWeights: [],\n",
" _losses: [],\n",
" _updates: [],\n",
" _built: false,\n",
" inboundNodes: [],\n",
" outboundNodes: [],\n",
" name: 'dense_Dense1',\n",
" trainable_: true,\n",
" batchInputShape: [ null, 1 ],\n",
" dtype: 'float32',\n",
" initialWeights: null,\n",
" _refCount: null,\n",
" fastWeightInitDuringBuild: false,\n",
" activation: Linear {},\n",
" useBias: true,\n",
" kernel: null,\n",
" bias: null,\n",
" DEFAULT_KERNEL_INITIALIZER: 'glorotNormal',\n",
" DEFAULT_BIAS_INITIALIZER: 'zeros',\n",
" units: 1,\n",
" kernelInitializer: GlorotNormal { scale: 1, mode: 'fanAvg', distribution: 'normal', seed: null },\n",
" biasInitializer: Zeros {},\n",
" kernelConstraint: null,\n",
" biasConstraint: null,\n",
" kernelRegularizer: null,\n",
" biasRegularizer: null }\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "XnSrQNAQaU92",
"colab_type": "text"
},
"source": [
"### Преобразуем слои в модель"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "REFKcQflaXg1",
"colab_type": "text"
},
"source": [
"Как только слои определены их необходимо преобразовать в модель. `Sequential`-модель принимает в качестве аргументов перечень слоёв в том порядке в котором их необходимо применять - от входного значения до выходного значения.\n",
"\n",
"У нашей модели всего один слой - `l0`."
]
},
{
"cell_type": "code",
"metadata": {
"id": "x80P7YO1ah-W",
"colab_type": "code",
"colab": {}
},
"source": [
"var model = tf.sequential({ layers: [l0] });"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "CE0fQzzza7fV",
"colab_type": "text"
},
"source": [
"**Примечание**\n",
"\n",
"Достаточно часто вы будете сталкиваться с определением слоёв прямо в функции модели, нежели с их предварительным описанием и последующим использованием:\n",
"\n",
"\n",
"\n",
"```\n",
"var model = tf.sequential([\n",
" tf.layers.dense({ units: 1, input_shape: [1] })\n",
"]);\n",
"```\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "y2qjhYVfbHjM",
"colab_type": "text"
},
"source": [
"## Компилируем модель с функцией потерь и оптимизаций"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "c6PdWU5sbMvZ",
"colab_type": "text"
},
"source": [
"Перед тренировкой модель должна быть скомпилирована (собрана). При компиляции для тренировки необходимы:\n",
"\n",
"* **функция потерь** - способ измерения того, насколько далеко предсказываемое значение от желаемого выходного значения (измеримая разница называется \"потерей\").\n",
"\n",
"* **функция оптимизации** - способ корректировки внутренних переменных для уменьшения потерь."
]
},
{
"cell_type": "code",
"metadata": {
"id": "9TjMYLVpbS7R",
"colab_type": "code",
"colab": {}
},
"source": [
"model.compile({\n",
" loss: 'meanSquaredError',\n",
" optimizer: tf.train.adam(0.1)\n",
"})"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "cCXkJJSXePxB",
"colab_type": "text"
},
"source": [
"Функция потерь и функция оптимизации используются во время тренировки модели (`model.fit(...)` упоминаемая ниже) для выполнения первичных вычислений в каждой точке и последующей оптимизации значений. \n",
"\n",
"Действие вычисления текущих потерь и последующее улучшение этих значений в модели - это именно то, чем является тренировка.\n",
"\n",
"Во время тренировки, функция оптимизации используется для подсчета корректировок значений внутренних переменных. Цель - подогнать значения внутренних переменных таким образом в модели (а это, по сути, математическая функция), чтобы те отражали непосредственное выражение конвертации градусов Цельсия в градусы Фаренгейта.\n",
"\n",
"TensorFlow использует численный анализ для выполнения подобного рода операций оптимизации и вся эта сложность скрыта от ваших глаз, поэтому мы не будем вдаваться в детали в этом курсе.\n",
"\n",
"Что полезно знать об этих параметрах:\n",
"\n",
"Функция потерь ([среднеквадратичная ошибка](https://www.yandex.ru/search/?text=%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D0%B5%D0%BA%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%B8%D1%87%D0%BD%D0%B0%D1%8F%20%D0%BE%D1%88%D0%B8%D0%B1%D0%BA%D0%B0&lr=192&clid=2186621)) и функция оптимизации ([Adam](https://machinelearningmastery.com/adam-optimization-algorithm-for-deep-learning/)), используемые в этом примере, являются стандартными для подобных простых моделей, но кроме них доступно множество других. На данном этапе нам не важно каким образом работают эти функции.\n",
"\n",
"На что стоит обратить внимание, так это на функцию оптимизации и параметр - коэффициент скорости обучения (learning rate), который в нашем примере равен `0.1`. Это используемый размер шага при корректировке внутренних значений переменных. Если значение слишком маленькое - понадобится слишком много обучающих итераций для обучения модели. Слишком большое - точность падает. Нахождение хорошего значения коэффициента скорости обучения требует некоторых проб и ошибок, оно обычно находится в интервале от `0.01` (по-умолчанию) до `0.1`."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "SzT5QtzxeUzJ",
"colab_type": "text"
},
"source": [
"### Тренируем модель\n",
"\n",
"Тренировка модели осуществляется методом `fit`.\n",
"\n",
"Во время тренировки модель получает на вход значения градусов Цельсия, выполняет преобразования используя значения внутренних переменных (называемые \"весами\") и возвращает значения, которые должны соответствовать градусами по Фаренгейту. Так как первоначальные значения весов установлены произвольными, то и результатирующие значения будут далеки от корректных значений. Разница между необходимым результатом и фактическим вычисляется с использованием функции потерь, а функция оптимизации определяет каким образом должны быть подкорректированы веса.\n",
"\n",
"Этот цикл вычислений, сравнений и корректировки контролируется внутри метода `fit`. Первый аргумент - входные значения, второй аргумент - желаемые выходные значения. Аргумент `epochs` определяет какое количество раз этот обучающий цикл должен быть выполнен. Аргумент `verbose` контролирует уровень логгирования."
]
},
{
"cell_type": "code",
"metadata": {
"id": "oUk-YVsrebUI",
"colab_type": "code",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 34
},
"outputId": "76402909-69f5-4e27-c56c-8506a1de1075"
},
"source": [
"run_async(async() => {\n",
" await model.fit(celsius_q, fahrenheit_a, {\n",
" epochs: 500,\n",
" verbose: 0\n",
" });\n",
" console.log(\"Завершили тренировку модели\");\n",
"});"
],
"execution_count": 22,
"outputs": [
{
"output_type": "stream",
"text": [
"Завершили тренировку модели\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "GGQ3ZVTbfNZO",
"colab_type": "text"
},
"source": [
"В последующих видео мы погрузимся в детали того, каким образом это всё работает и как именно работают плотные слои (Dense-слои) \"под капотом\"."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "6qFBvao6fP9R",
"colab_type": "text"
},
"source": [
"### Отображаем статистику тренировок\n",
"\n",
"Метод `fit` возвращает объект, который содержит информацию об изменении потерь с каждой последующей итерацией. Мы можем воспользоваться этим объектом для построения соответствующего графика потерь. Высокая потеря означает, что значение градусов Фаренгейта, которые предсказала модель, далеки от истинных значений в массиве `fahrenheit_a`.\n",
"\n",
"Для визуализации воспользуемся `Matplotlib`. Как вы можете увидеть, наша модель улучшается очень быстро в самом начале, а затем приходит к стабильному и медленному улучшению до тех пор, пока результаты не становятся \"около\"-идеальными в самом конце обучения."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "gm48BATFfTsa",
"colab_type": "text"
},
"source": [
"*** TO BE DONE ***"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "4-X4QAlKfa0U",
"colab_type": "text"
},
"source": [
"### Используем модель для предсказаний\n",
"\n",
"Теперь у нас есть модель, которая была обучена на входных значениях `celsius_q` и выходных значениях `fahrenheit_a` для определения взаимосвязи между ними. Мы можем воспользоваться методом предсказания для вычисления тех значений градусов Фаренгейта по которым ранее нам неизвестны были соответствующие градусы Цельсия.\n",
"\n",
"Например, сколько будет `100.0` градусов Цельсия по Фаренгейту? Попробуйте угадать перед тем как запускать код ниже."
]
},
{
"cell_type": "code",
"metadata": {
"id": "jGqti1Cdffji",
"colab_type": "code",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 34
},
"outputId": "ed2b5ea2-ab0a-4778-c6de-ab437f93cbb0"
},
"source": [
"console.log(\n",
" model.predict(tf.tensor([100.0])).dataSync()\n",
")"
],
"execution_count": 32,
"outputs": [
{
"output_type": "stream",
"text": [
"Float32Array [ 211.29966735839844 ]\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "Wfd1Iyt0fuEZ",
"colab_type": "text"
},
"source": [
"Правильный ответ $100 \\times 1.8 + 32 = 212$, так что наша модель справилась достаточно хорошо!"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "TGEgyiL6fxfM",
"colab_type": "text"
},
"source": [
"### Ревью\n",
"\n",
"* Мы создали модель с использованием Dense-слоя\n",
"* Мы обучили её на 3500 примерах (7 пар значений, 500 обучающих итераций)\n",
"\n",
"Наша модель подогнала значения внутренних переменных (весов) в Dense-слое таким образом, чтобы возвращать правильные значения градусов Фаренгейта на произвольное входное значение градусов Цельсия. "
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "XRDsnbl1fz75",
"colab_type": "text"
},
"source": [
"## Смотрим на веса в слое"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "O-WfqDTCf2rQ",
"colab_type": "text"
},
"source": [
"Давайте отобразим значения внутренних переменных Dense-слоя."
]
},
{
"cell_type": "code",
"metadata": {
"id": "zXkxcC2Sf5F1",
"colab_type": "code",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 34
},
"outputId": "a31952e6-aac0-4110-fbe5-5d3298bfcdca"
},
"source": [
"console.log(\"Это значения переменных слоя: \" + JSON.stringify(l0.getWeights().map(d => d.dataSync())))"
],
"execution_count": 44,
"outputs": [
{
"output_type": "stream",
"text": [
"Это значения переменных слоя: [{\"0\":1.8256988525390625},{\"0\":28.72978401184082}]\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "1XoqbeLXgVWP",
"colab_type": "text"
},
"source": [
"Значение первой переменной близко к ~1.8 , а второй к ~32. Эти значения (1.8 и 32) являются непосредственными значениями в формуле конвертации градусов Цельсия в градусы Фаренгейта.\n",
"\n",
"Это действительно очень близко к фактическим значениям в формуле! Мы рассмотрим этот момент подробнее в последующих видео, где мы покажем, каким образом работает Dense-слой, а пока стоит знать лишь то, что один нейрон с единственным входом и выходом, содержит в себе простую математику - `y = mx + b` (уравнение прямой), которая представляет собой не что иное, как нашу с вами формулу преобразования градусов Цельсия в градусы Фаренгейта, `f = 1.8c + 32`.\n",
"\n",
"Так как представления одинаковые, то и значения внутренних переменных модели должны были сойтись к тем, которые представлены в фактической формуле, что и произошло в итоге.\n",
"\n",
"При наличие дополнительных нейронов, дополнительных входных значений и выходных значений, формула становится немного сложнее, но суть остаётся той же."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "Km3Zr7_bgdPn",
"colab_type": "text"
},
"source": [
"### Немного экспериментов\n",
"\n",
"Ради веселья! Что будет, если мы создадим больше Dense-слоёв с большим количеством нейронов, которые, в свою очередь, будут содержать больше внутренних переменных?"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "l3_b0-FSgeDS",
"colab_type": "text"
},
"source": [
"*** TO BE DONE *** "
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment