-Поиск по дневнику

Поиск сообщений в rss_rss_hh_full

 -Подписка по e-mail

 

 -Постоянные читатели

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 17.03.2011
Записей:
Комментариев:
Написано: 1

Habrahabr








Добавить любой RSS - источник (включая журнал LiveJournal) в свою ленту друзей вы можете на странице синдикации.

Исходная информация - http://habrahabr.ru/rss/.
Данный дневник сформирован из открытого RSS-источника по адресу http://feeds.feedburner.com/xtmb/hh-full, и дополняется в соответствии с дополнением данного источника. Он может не соответствовать содержимому оригинальной страницы. Трансляция создана автоматически по запросу читателей этой RSS ленты.
По всем вопросам о работе данного сервиса обращаться со страницы контактной информации.

[Обновить трансляцию]

[Из песочницы] Как написать свой первый Linux device driver

Четверг, 14 Сентября 2017 г. 15:50 + в цитатник
Alexeynew сегодня в 15:50 Разработка

Как написать свой первый Linux device driver

Здравствуйте, дорогие хабрачитатели.

Цель данной статьи — показать принцип реализации драйверов устройств в системе Linux, на примере простого символьного драйвера.

Для меня же, главной целью является подвести итог и сформировать базовые знания для написания будущих модулей ядра, а также получить опыт изложение технической литературы для публики, т.к через пол года я буду выступать со своим дипломным проектом (да я студент).

Это моя первая статья, пожалуйста не судите строго!

P.S

Получилось слишком много букв, поэтому я принял решение разделить статью на три части:

Часть 1 — Введение, инициализация и очистка модуля ядра.
Часть 2 — Функции open, read, write и trim.
Часть 3 — Пишем Makefile и тестируем устройство.

Перед вступлением, хочу сказать, что здесь будут изложены базовые вещи, более подробная информация будет изложена во второй и последней части данной статьи.

Итак, начнем.

Подготовительные работы


Символьный драйвер (Char driver) — это, драйвер, который оперирует/работает с выделенной ядром областью памяти.

Драйвер представляет каждое устройство структурой scull_dev, а также интерфейс cdev к ядру.

struct scull_dev {
	struct scull_qset *data;  /* Указатель на первый кусок памяти */
	int quantum;		  /* Размер одного кванта памяти */
	int qset;		  /* Количество таких квантов */
	unsigned long size;	  /* Размер используемой памяти */
	struct semaphore sem;     /* Используется семафорами */
	struct cdev cdev;	  /* Структура, представляющая символьные устройства */
};

struct scull_dev *scull_device;

Устройство будет представлять связный список указателей, каждый из которых указывает на структуру scull_qset.

struct scull_qset {
	void **data;
	struct scull_qset *next;
};

Для наглядности посмотрите на картинку.

image

Для регистрации устройства, нужно задать специальные номера, а именно:

MAJOR — старший номер (является уникальным в системе).
MINOR — младший номер (не является уникальным в системе).

В ядре есть механизм, который позволяет регистрировать специализированные номера вручную, но такой подход не желателен и лучше вежливо попросить ядро динамически выделить их для нас, пример кода будет ниже.

После того как мы определили номера для нашего устройства, мы должны установить связь между этими номерами и операциями драйвера. Это можно сделать используя структуру file_operations.

struct file_operations scull_fops = {
	.owner = THIS_MODULE,
	.read = scull_read,
	.write = scull_write,
	.open = scull_open,
	.release = scull_release,
};

В ядре есть специальные макросы module_init/module_exit, которые указывают путь к функциям инициализации/удаления модуля. Без этих определений функции инициализации/удаления никогда не будут вызваны.

module_init(scull_init_module);
module_exit(scull_cleanup_module);

Здесь будем хранить базовую информацию об устройстве.

int scull_major = 0;		/* MAJOR номер*/
int scull_minor = 0;		/* MINOR номер*/
int scull_nr_devs = 1;		/* Количество регистрируемых устройств */
int scull_quantum = 4000;	/* Размера памяти в байтах */
int scull_qset = 1000;		/* Количество квантов памяти */

Последним этапом подготовительной работы будет подключение библиотек.
Краткое описание приведено ниже, но если вы хотите копнуть поглубже, то добро пожаловать на прекрасны сайт: lxr

#include module.h> /* Содержит функции и определения для динамической загрузки модулей ядра */
#include init.h>  /* Указывает на функции инициализации и очистки */
#include fs.h>    /* Содержит функции регистрации и удаления драйвера */
#include cdev.h>  /* Содержит необходимые функции для символьного драйвера */
#include slab.h>  /* Содержит функцию ядра для управления памятью */
#include uaccess.h> /* Предоставляет доступ к пространству пользователя */

Инициализация


Теперь давайте посмотрим на функцию инициализации устройства.

static int scull_init_module(void)
{
	int rv, i;
	dev_t dev;

	rv = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull");	
	scull_major = MAJOR(dev);

	if (rv) {
		printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
		return rv;
	}

	scull_device = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL);
	
	if (!scull_device) {
		rv = -ENOMEM;
		goto fail;
	}

	memset(scull_device, 0, scull_nr_devs * sizeof(struct scull_dev));

	for (i = 0; i < scull_nr_devs; i++) {
		scull_device[i].quantum = scull_quantum;
		scull_device[i].qset = scull_qset;
		sema_init(&scull_device[i].sem, 1);
		scull_setup_cdev(&scull_device[i], i);
	}

	dev = MKDEV(scull_major, scull_minor + scull_nr_devs);	

	return 0;

fail:
	scull_cleanup_module();
	return rv;
}

Первым делом, вызывая alloc_chrdev_region мы регистрируем диапазон символьных номеров устройств и указываем имя устройства. После вызовом MAJOR(dev) мы получаем старший номер.
Далее проверяется вернувшееся значение, если оно является кодом ошибки, то выходим из функции. Стоит отметить, что при разработке реального драйвера устройства следует всегда проверять возвращаемые значения, а также указатели на любые элементы (NULL?).

rv = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull");	
	scull_major = MAJOR(dev);

if (rv) {
	printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
	return rv;
}

Если вернувшееся значение не является кодом ошибки мы может продолжить выполнять инициализацию.

Выделяем память, делая системный вызов kmalloc и обязательно проверяем указатель NULL.

scull_device = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL);
	
if (!scull_device) {
	rv = -ENOMEM;
	goto fail;
}

memset(scull_device, 0, scull_nr_devs * sizeof(struct scull_dev));

Продолжаем инициализацию. Главная здесь функция — это scull_setup_cdev, о ней мы поговорим чуть ниже. MKDEV служит для хранения старший и младших номеров устройств.

for (i = 0; i < scull_nr_devs; i++) {
		scull_device[i].quantum = scull_quantum;
		scull_device[i].qset = scull_qset;
		sema_init(&scull_device[i].sem, 1);
		scull_setup_cdev(&scull_device[i], i);
	}

	dev = MKDEV(scull_major, scull_minor + scull_nr_devs);

Возвращаем значение или обрабатываем ошибку и удаляем устройство.

return 0;

fail:
	scull_cleanup_module();
	return rv;
}

Выше были представлены структуры scull_dev и cdev, которая реализует интерфейс между нашим устройством и ядром. Функция scull_setup_cdev выполняет инициализацию и добавление структуры в систему.

static void scull_setup_cdev(struct scull_dev *dev, int index)
{
	int err, devno = MKDEV(scull_major, scull_minor + index);

	cdev_init(&dev->cdev, &scull_fops); 

	dev->cdev.owner = THIS_MODULE;
	dev->cdev.ops = &scull_fops;

	err = cdev_add(&dev->cdev, devno, 1);

	if (err)
		printk(KERN_NOTICE "Error %d adding scull  %d", err, index);
}

Удаление


Функция scull_cleanup_module вызывается при удаление модуля устройства из ядра.
Обратный процесс инициализации, удаляем структуры устройств, освобождаем память и удаляем выделенные ядром младшие и старшие номера.

void scull_cleanup_module(void)
{
	int i;
	dev_t devno = MKDEV(scull_major, scull_minor);

	if (scull_device) {
		for (i = 0; i < scull_nr_devs; i++) {
			scull_trim(scull_device + i);
			cdev_del(&scull_device[i].cdev);	
		}
		
		kfree(scull_device);
	}

	unregister_chrdev_region(devno, scull_nr_devs); 
}

Полный код
#include module.h> 	
#include init.h> 	
#include fs.h> 		
#include cdev.h> 	
#include slab.h> 	
#include uaccess.h>

int scull_major = 0;		
int scull_minor = 0;		
int scull_nr_devs = 1;		
int scull_quantum = 4000;	
int scull_qset = 1000;	

struct scull_qset {
	void **data;			
	struct scull_qset *next; 	
};

struct scull_dev {
	struct scull_qset *data;  
	int quantum;		 
	int qset;		  
	unsigned long size;	  
	unsigned int access_key;  
	struct semaphore sem;    
	struct cdev cdev;	 
};

struct scull_dev *scull_device;

int scull_trim(struct scull_dev *dev)
{
	struct scull_qset *next, *dptr;
	int qset = dev->qset; 
	int i;

	for (dptr = dev->data; dptr; dptr = next) { 
		if (dptr->data) {
			for (i = 0; i < qset; i++)
				kfree(dptr->data[i]);

			kfree(dptr->data);
			dptr->data = NULL;
		}

		next = dptr->next;
		kfree(dptr);
	}

	dev->size = 0;
	dev->quantum = scull_quantum;
	dev->qset = scull_qset;
	dev->data = NULL;

	return 0;
}

struct file_operations scull_fops = {		
	.owner = THIS_MODULE,			
	//.read = scull_read,
	//.write = scull_write,
	//.open = scull_open,
	//.release = scull_release,
};

static void scull_setup_cdev(struct scull_dev *dev, int index)
{
	int err, devno = MKDEV(scull_major, scull_minor + index);	

	cdev_init(&dev->cdev, &scull_fops);

	dev->cdev.owner = THIS_MODULE;
	dev->cdev.ops = &scull_fops;

	err = cdev_add(&dev->cdev, devno, 1);

	if (err)
		printk(KERN_NOTICE "Error %d adding scull  %d", err, index);
}

void scull_cleanup_module(void)
{
	int i;
	dev_t devno = MKDEV(scull_major, scull_minor);

	if (scull_device) {
		for (i = 0; i < scull_nr_devs; i++) {
			scull_trim(scull_device + i);		
			cdev_del(&scull_device[i].cdev);	
		}
		
		kfree(scull_device);
	}

	unregister_chrdev_region(devno, scull_nr_devs); 
}

static int scull_init_module(void)
{
	int rv, i;
	dev_t dev;

		
	rv = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull");	
	scull_major = MAJOR(dev);

	if (rv) {
		printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
		return rv;
	}

	scull_device = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL);	
	
	if (!scull_device) {
		rv = -ENOMEM;
		goto fail;
	}

	memset(scull_device, 0, scull_nr_devs * sizeof(struct scull_dev));		

	for (i = 0; i < scull_nr_devs; i++) {						
		scull_device[i].quantum = scull_quantum;
		scull_device[i].qset = scull_qset;
		sema_init(&scull_device[i].sem, 1);
		scull_setup_cdev(&scull_device[i], i);					
	}

	dev = MKDEV(scull_major, scull_minor + scull_nr_devs);	
	
	printk(KERN_INFO "scull: major = %d minor = %d\n", scull_major, scull_minor);

	return 0;

fail:
	scull_cleanup_module();
	return rv;
}

MODULE_AUTHOR("Your name");
MODULE_LICENSE("GPL");

module_init(scull_init_module);		
module_exit(scull_cleanup_module);	


С удовольствием выслушаю конструктивную критику и буду ждать feedback'a.

Если вы нашли ошибки или я не правильно изложил материал, пожалуйста, укажите мне на это.
Для более быстрой реакции пишите в ЛС.

Спасибо!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/337946/


Метки:  

[Из песочницы] Как написать свой первый Linux device driver

Четверг, 14 Сентября 2017 г. 15:50 + в цитатник
Alexeynew сегодня в 15:50 Разработка

Как написать свой первый Linux device driver

Здравствуйте, дорогие хабрачитатели.

Цель данной статьи — показать принцип реализации драйверов устройств в системе Linux, на примере простого символьного драйвера.

Для меня же, главной целью является подвести итог и сформировать базовые знания для написания будущих модулей ядра, а также получить опыт изложение технической литературы для публики, т.к через пол года я буду выступать со своим дипломным проектом (да я студент).

Это моя первая статья, пожалуйста не судите строго!

P.S

Получилось слишком много букв, поэтому я принял решение разделить статью на три части:

Часть 1 — Введение, инициализация и очистка модуля ядра.
Часть 2 — Функции open, read, write и trim.
Часть 3 — Пишем Makefile и тестируем устройство.

Перед вступлением, хочу сказать, что здесь будут изложены базовые вещи, более подробная информация будет изложена во второй и последней части данной статьи.

Итак, начнем.

Подготовительные работы


Символьный драйвер (Char driver) — это, драйвер, который оперирует/работает с выделенной ядром областью памяти.

Драйвер представляет каждое устройство структурой scull_dev, а также интерфейс cdev к ядру.

struct scull_dev {
	struct scull_qset *data;  /* Указатель на первый кусок памяти */
	int quantum;		  /* Размер одного кванта памяти */
	int qset;		  /* Количество таких квантов */
	unsigned long size;	  /* Размер используемой памяти */
	struct semaphore sem;     /* Используется семафорами */
	struct cdev cdev;	  /* Структура, представляющая символьные устройства */
};

struct scull_dev *scull_device;

Устройство будет представлять связный список указателей, каждый из которых указывает на структуру scull_qset.

struct scull_qset {
	void **data;
	struct scull_qset *next;
};

Для наглядности посмотрите на картинку.

image

Для регистрации устройства, нужно задать специальные номера, а именно:

MAJOR — старший номер (является уникальным в системе).
MINOR — младший номер (не является уникальным в системе).

В ядре есть механизм, который позволяет регистрировать специализированные номера вручную, но такой подход не желателен и лучше вежливо попросить ядро динамически выделить их для нас, пример кода будет ниже.

После того как мы определили номера для нашего устройства, мы должны установить связь между этими номерами и операциями драйвера. Это можно сделать используя структуру file_operations.

struct file_operations scull_fops = {
	.owner = THIS_MODULE,
	.read = scull_read,
	.write = scull_write,
	.open = scull_open,
	.release = scull_release,
};

В ядре есть специальные макросы module_init/module_exit, которые указывают путь к функциям инициализации/удаления модуля. Без этих определений функции инициализации/удаления никогда не будут вызваны.

module_init(scull_init_module);
module_exit(scull_cleanup_module);

Здесь будем хранить базовую информацию об устройстве.

int scull_major = 0;		/* MAJOR номер*/
int scull_minor = 0;		/* MINOR номер*/
int scull_nr_devs = 1;		/* Количество регистрируемых устройств */
int scull_quantum = 4000;	/* Размера памяти в байтах */
int scull_qset = 1000;		/* Количество квантов памяти */

Последним этапом подготовительной работы будет подключение библиотек.
Краткое описание приведено ниже, но если вы хотите копнуть поглубже, то добро пожаловать на прекрасны сайт: lxr

#include module.h> /* Содержит функции и определения для динамической загрузки модулей ядра */
#include init.h>  /* Указывает на функции инициализации и очистки */
#include fs.h>    /* Содержит функции регистрации и удаления драйвера */
#include cdev.h>  /* Содержит необходимые функции для символьного драйвера */
#include slab.h>  /* Содержит функцию ядра для управления памятью */
#include uaccess.h> /* Предоставляет доступ к пространству пользователя */

Инициализация


Теперь давайте посмотрим на функцию инициализации устройства.

static int scull_init_module(void)
{
	int rv, i;
	dev_t dev;

	rv = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull");	
	scull_major = MAJOR(dev);

	if (rv) {
		printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
		return rv;
	}

	scull_device = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL);
	
	if (!scull_device) {
		rv = -ENOMEM;
		goto fail;
	}

	memset(scull_device, 0, scull_nr_devs * sizeof(struct scull_dev));

	for (i = 0; i < scull_nr_devs; i++) {
		scull_device[i].quantum = scull_quantum;
		scull_device[i].qset = scull_qset;
		sema_init(&scull_device[i].sem, 1);
		scull_setup_cdev(&scull_device[i], i);
	}

	dev = MKDEV(scull_major, scull_minor + scull_nr_devs);	

	return 0;

fail:
	scull_cleanup_module();
	return rv;
}

Первым делом, вызывая alloc_chrdev_region мы регистрируем диапазон символьных номеров устройств и указываем имя устройства. После вызовом MAJOR(dev) мы получаем старший номер.
Далее проверяется вернувшееся значение, если оно является кодом ошибки, то выходим из функции. Стоит отметить, что при разработке реального драйвера устройства следует всегда проверять возвращаемые значения, а также указатели на любые элементы (NULL?).

rv = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull");	
	scull_major = MAJOR(dev);

if (rv) {
	printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
	return rv;
}

Если вернувшееся значение не является кодом ошибки мы может продолжить выполнять инициализацию.

Выделяем память, делая системный вызов kmalloc и обязательно проверяем указатель NULL.

scull_device = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL);
	
if (!scull_device) {
	rv = -ENOMEM;
	goto fail;
}

memset(scull_device, 0, scull_nr_devs * sizeof(struct scull_dev));

Продолжаем инициализацию. Главная здесь функция — это scull_setup_cdev, о ней мы поговорим чуть ниже. MKDEV служит для хранения старший и младших номеров устройств.

for (i = 0; i < scull_nr_devs; i++) {
		scull_device[i].quantum = scull_quantum;
		scull_device[i].qset = scull_qset;
		sema_init(&scull_device[i].sem, 1);
		scull_setup_cdev(&scull_device[i], i);
	}

	dev = MKDEV(scull_major, scull_minor + scull_nr_devs);

Возвращаем значение или обрабатываем ошибку и удаляем устройство.

return 0;

fail:
	scull_cleanup_module();
	return rv;
}

Выше были представлены структуры scull_dev и cdev, которая реализует интерфейс между нашим устройством и ядром. Функция scull_setup_cdev выполняет инициализацию и добавление структуры в систему.

static void scull_setup_cdev(struct scull_dev *dev, int index)
{
	int err, devno = MKDEV(scull_major, scull_minor + index);

	cdev_init(&dev->cdev, &scull_fops); 

	dev->cdev.owner = THIS_MODULE;
	dev->cdev.ops = &scull_fops;

	err = cdev_add(&dev->cdev, devno, 1);

	if (err)
		printk(KERN_NOTICE "Error %d adding scull  %d", err, index);
}

Удаление


Функция scull_cleanup_module вызывается при удаление модуля устройства из ядра.
Обратный процесс инициализации, удаляем структуры устройств, освобождаем память и удаляем выделенные ядром младшие и старшие номера.

void scull_cleanup_module(void)
{
	int i;
	dev_t devno = MKDEV(scull_major, scull_minor);

	if (scull_device) {
		for (i = 0; i < scull_nr_devs; i++) {
			scull_trim(scull_device + i);
			cdev_del(&scull_device[i].cdev);	
		}
		
		kfree(scull_device);
	}

	unregister_chrdev_region(devno, scull_nr_devs); 
}

Полный код
#include module.h> 	
#include init.h> 	
#include fs.h> 		
#include cdev.h> 	
#include slab.h> 	
#include uaccess.h>

int scull_major = 0;		
int scull_minor = 0;		
int scull_nr_devs = 1;		
int scull_quantum = 4000;	
int scull_qset = 1000;	

struct scull_qset {
	void **data;			
	struct scull_qset *next; 	
};

struct scull_dev {
	struct scull_qset *data;  
	int quantum;		 
	int qset;		  
	unsigned long size;	  
	unsigned int access_key;  
	struct semaphore sem;    
	struct cdev cdev;	 
};

struct scull_dev *scull_device;

int scull_trim(struct scull_dev *dev)
{
	struct scull_qset *next, *dptr;
	int qset = dev->qset; 
	int i;

	for (dptr = dev->data; dptr; dptr = next) { 
		if (dptr->data) {
			for (i = 0; i < qset; i++)
				kfree(dptr->data[i]);

			kfree(dptr->data);
			dptr->data = NULL;
		}

		next = dptr->next;
		kfree(dptr);
	}

	dev->size = 0;
	dev->quantum = scull_quantum;
	dev->qset = scull_qset;
	dev->data = NULL;

	return 0;
}

struct file_operations scull_fops = {		
	.owner = THIS_MODULE,			
	//.read = scull_read,
	//.write = scull_write,
	//.open = scull_open,
	//.release = scull_release,
};

static void scull_setup_cdev(struct scull_dev *dev, int index)
{
	int err, devno = MKDEV(scull_major, scull_minor + index);	

	cdev_init(&dev->cdev, &scull_fops);

	dev->cdev.owner = THIS_MODULE;
	dev->cdev.ops = &scull_fops;

	err = cdev_add(&dev->cdev, devno, 1);

	if (err)
		printk(KERN_NOTICE "Error %d adding scull  %d", err, index);
}

void scull_cleanup_module(void)
{
	int i;
	dev_t devno = MKDEV(scull_major, scull_minor);

	if (scull_device) {
		for (i = 0; i < scull_nr_devs; i++) {
			scull_trim(scull_device + i);		
			cdev_del(&scull_device[i].cdev);	
		}
		
		kfree(scull_device);
	}

	unregister_chrdev_region(devno, scull_nr_devs); 
}

static int scull_init_module(void)
{
	int rv, i;
	dev_t dev;

		
	rv = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull");	
	scull_major = MAJOR(dev);

	if (rv) {
		printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
		return rv;
	}

	scull_device = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL);	
	
	if (!scull_device) {
		rv = -ENOMEM;
		goto fail;
	}

	memset(scull_device, 0, scull_nr_devs * sizeof(struct scull_dev));		

	for (i = 0; i < scull_nr_devs; i++) {						
		scull_device[i].quantum = scull_quantum;
		scull_device[i].qset = scull_qset;
		sema_init(&scull_device[i].sem, 1);
		scull_setup_cdev(&scull_device[i], i);					
	}

	dev = MKDEV(scull_major, scull_minor + scull_nr_devs);	
	
	printk(KERN_INFO "scull: major = %d minor = %d\n", scull_major, scull_minor);

	return 0;

fail:
	scull_cleanup_module();
	return rv;
}

MODULE_AUTHOR("Your name");
MODULE_LICENSE("GPL");

module_init(scull_init_module);		
module_exit(scull_cleanup_module);	


С удовольствием выслушаю конструктивную критику и буду ждать feedback'a.

Если вы нашли ошибки или я не правильно изложил материал, пожалуйста, укажите мне на это.
Для более быстрой реакции пишите в ЛС.

Спасибо!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/337946/


Метки:  

[Из песочницы] Как написать свой первый Linux device driver

Четверг, 14 Сентября 2017 г. 15:50 + в цитатник
Alexeynew сегодня в 15:50 Разработка

Как написать свой первый Linux device driver

Здравствуйте, дорогие хабрачитатели.

Цель данной статьи — показать принцип реализации драйверов устройств в системе Linux, на примере простого символьного драйвера.

Для меня же, главной целью является подвести итог и сформировать базовые знания для написания будущих модулей ядра, а также получить опыт изложение технической литературы для публики, т.к через пол года я буду выступать со своим дипломным проектом (да я студент).

Это моя первая статья, пожалуйста не судите строго!

P.S

Получилось слишком много букв, поэтому я принял решение разделить статью на три части:

Часть 1 — Введение, инициализация и очистка модуля ядра.
Часть 2 — Функции open, read, write и trim.
Часть 3 — Пишем Makefile и тестируем устройство.

Перед вступлением, хочу сказать, что здесь будут изложены базовые вещи, более подробная информация будет изложена во второй и последней части данной статьи.

Итак, начнем.

Подготовительные работы


Символьный драйвер (Char driver) — это, драйвер, который оперирует/работает с выделенной ядром областью памяти.

Драйвер представляет каждое устройство структурой scull_dev, а также интерфейс cdev к ядру.

struct scull_dev {
	struct scull_qset *data;  /* Указатель на первый кусок памяти */
	int quantum;		  /* Размер одного кванта памяти */
	int qset;		  /* Количество таких квантов */
	unsigned long size;	  /* Размер используемой памяти */
	struct semaphore sem;     /* Используется семафорами */
	struct cdev cdev;	  /* Структура, представляющая символьные устройства */
};

struct scull_dev *scull_device;

Устройство будет представлять связный список указателей, каждый из которых указывает на структуру scull_qset.

struct scull_qset {
	void **data;
	struct scull_qset *next;
};

Для наглядности посмотрите на картинку.

image

Для регистрации устройства, нужно задать специальные номера, а именно:

MAJOR — старший номер (является уникальным в системе).
MINOR — младший номер (не является уникальным в системе).

В ядре есть механизм, который позволяет регистрировать специализированные номера вручную, но такой подход не желателен и лучше вежливо попросить ядро динамически выделить их для нас, пример кода будет ниже.

После того как мы определили номера для нашего устройства, мы должны установить связь между этими номерами и операциями драйвера. Это можно сделать используя структуру file_operations.

struct file_operations scull_fops = {
	.owner = THIS_MODULE,
	.read = scull_read,
	.write = scull_write,
	.open = scull_open,
	.release = scull_release,
};

В ядре есть специальные макросы module_init/module_exit, которые указывают путь к функциям инициализации/удаления модуля. Без этих определений функции инициализации/удаления никогда не будут вызваны.

module_init(scull_init_module);
module_exit(scull_cleanup_module);

Здесь будем хранить базовую информацию об устройстве.

int scull_major = 0;		/* MAJOR номер*/
int scull_minor = 0;		/* MINOR номер*/
int scull_nr_devs = 1;		/* Количество регистрируемых устройств */
int scull_quantum = 4000;	/* Размера памяти в байтах */
int scull_qset = 1000;		/* Количество квантов памяти */

Последним этапом подготовительной работы будет подключение библиотек.
Краткое описание приведено ниже, но если вы хотите копнуть поглубже, то добро пожаловать на прекрасны сайт: lxr

#include module.h> /* Содержит функции и определения для динамической загрузки модулей ядра */
#include init.h>  /* Указывает на функции инициализации и очистки */
#include fs.h>    /* Содержит функции регистрации и удаления драйвера */
#include cdev.h>  /* Содержит необходимые функции для символьного драйвера */
#include slab.h>  /* Содержит функцию ядра для управления памятью */
#include uaccess.h> /* Предоставляет доступ к пространству пользователя */

Инициализация


Теперь давайте посмотрим на функцию инициализации устройства.

static int scull_init_module(void)
{
	int rv, i;
	dev_t dev;

	rv = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull");	
	scull_major = MAJOR(dev);

	if (rv) {
		printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
		return rv;
	}

	scull_device = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL);
	
	if (!scull_device) {
		rv = -ENOMEM;
		goto fail;
	}

	memset(scull_device, 0, scull_nr_devs * sizeof(struct scull_dev));

	for (i = 0; i < scull_nr_devs; i++) {
		scull_device[i].quantum = scull_quantum;
		scull_device[i].qset = scull_qset;
		sema_init(&scull_device[i].sem, 1);
		scull_setup_cdev(&scull_device[i], i);
	}

	dev = MKDEV(scull_major, scull_minor + scull_nr_devs);	

	return 0;

fail:
	scull_cleanup_module();
	return rv;
}

Первым делом, вызывая alloc_chrdev_region мы регистрируем диапазон символьных номеров устройств и указываем имя устройства. После вызовом MAJOR(dev) мы получаем старший номер.
Далее проверяется вернувшееся значение, если оно является кодом ошибки, то выходим из функции. Стоит отметить, что при разработке реального драйвера устройства следует всегда проверять возвращаемые значения, а также указатели на любые элементы (NULL?).

rv = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull");	
	scull_major = MAJOR(dev);

if (rv) {
	printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
	return rv;
}

Если вернувшееся значение не является кодом ошибки мы может продолжить выполнять инициализацию.

Выделяем память, делая системный вызов kmalloc и обязательно проверяем указатель NULL.

scull_device = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL);
	
if (!scull_device) {
	rv = -ENOMEM;
	goto fail;
}

memset(scull_device, 0, scull_nr_devs * sizeof(struct scull_dev));

Продолжаем инициализацию. Главная здесь функция — это scull_setup_cdev, о ней мы поговорим чуть ниже. MKDEV служит для хранения старший и младших номеров устройств.

for (i = 0; i < scull_nr_devs; i++) {
		scull_device[i].quantum = scull_quantum;
		scull_device[i].qset = scull_qset;
		sema_init(&scull_device[i].sem, 1);
		scull_setup_cdev(&scull_device[i], i);
	}

	dev = MKDEV(scull_major, scull_minor + scull_nr_devs);

Возвращаем значение или обрабатываем ошибку и удаляем устройство.

return 0;

fail:
	scull_cleanup_module();
	return rv;
}

Выше были представлены структуры scull_dev и cdev, которая реализует интерфейс между нашим устройством и ядром. Функция scull_setup_cdev выполняет инициализацию и добавление структуры в систему.

static void scull_setup_cdev(struct scull_dev *dev, int index)
{
	int err, devno = MKDEV(scull_major, scull_minor + index);

	cdev_init(&dev->cdev, &scull_fops); 

	dev->cdev.owner = THIS_MODULE;
	dev->cdev.ops = &scull_fops;

	err = cdev_add(&dev->cdev, devno, 1);

	if (err)
		printk(KERN_NOTICE "Error %d adding scull  %d", err, index);
}

Удаление


Функция scull_cleanup_module вызывается при удаление модуля устройства из ядра.
Обратный процесс инициализации, удаляем структуры устройств, освобождаем память и удаляем выделенные ядром младшие и старшие номера.

void scull_cleanup_module(void)
{
	int i;
	dev_t devno = MKDEV(scull_major, scull_minor);

	if (scull_device) {
		for (i = 0; i < scull_nr_devs; i++) {
			scull_trim(scull_device + i);
			cdev_del(&scull_device[i].cdev);	
		}
		
		kfree(scull_device);
	}

	unregister_chrdev_region(devno, scull_nr_devs); 
}

Полный код
#include module.h> 	
#include init.h> 	
#include fs.h> 		
#include cdev.h> 	
#include slab.h> 	
#include uaccess.h>

int scull_major = 0;		
int scull_minor = 0;		
int scull_nr_devs = 1;		
int scull_quantum = 4000;	
int scull_qset = 1000;	

struct scull_qset {
	void **data;			
	struct scull_qset *next; 	
};

struct scull_dev {
	struct scull_qset *data;  
	int quantum;		 
	int qset;		  
	unsigned long size;	  
	unsigned int access_key;  
	struct semaphore sem;    
	struct cdev cdev;	 
};

struct scull_dev *scull_device;

int scull_trim(struct scull_dev *dev)
{
	struct scull_qset *next, *dptr;
	int qset = dev->qset; 
	int i;

	for (dptr = dev->data; dptr; dptr = next) { 
		if (dptr->data) {
			for (i = 0; i < qset; i++)
				kfree(dptr->data[i]);

			kfree(dptr->data);
			dptr->data = NULL;
		}

		next = dptr->next;
		kfree(dptr);
	}

	dev->size = 0;
	dev->quantum = scull_quantum;
	dev->qset = scull_qset;
	dev->data = NULL;

	return 0;
}

struct file_operations scull_fops = {		
	.owner = THIS_MODULE,			
	//.read = scull_read,
	//.write = scull_write,
	//.open = scull_open,
	//.release = scull_release,
};

static void scull_setup_cdev(struct scull_dev *dev, int index)
{
	int err, devno = MKDEV(scull_major, scull_minor + index);	

	cdev_init(&dev->cdev, &scull_fops);

	dev->cdev.owner = THIS_MODULE;
	dev->cdev.ops = &scull_fops;

	err = cdev_add(&dev->cdev, devno, 1);

	if (err)
		printk(KERN_NOTICE "Error %d adding scull  %d", err, index);
}

void scull_cleanup_module(void)
{
	int i;
	dev_t devno = MKDEV(scull_major, scull_minor);

	if (scull_device) {
		for (i = 0; i < scull_nr_devs; i++) {
			scull_trim(scull_device + i);		
			cdev_del(&scull_device[i].cdev);	
		}
		
		kfree(scull_device);
	}

	unregister_chrdev_region(devno, scull_nr_devs); 
}

static int scull_init_module(void)
{
	int rv, i;
	dev_t dev;

		
	rv = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull");	
	scull_major = MAJOR(dev);

	if (rv) {
		printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
		return rv;
	}

	scull_device = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL);	
	
	if (!scull_device) {
		rv = -ENOMEM;
		goto fail;
	}

	memset(scull_device, 0, scull_nr_devs * sizeof(struct scull_dev));		

	for (i = 0; i < scull_nr_devs; i++) {						
		scull_device[i].quantum = scull_quantum;
		scull_device[i].qset = scull_qset;
		sema_init(&scull_device[i].sem, 1);
		scull_setup_cdev(&scull_device[i], i);					
	}

	dev = MKDEV(scull_major, scull_minor + scull_nr_devs);	
	
	printk(KERN_INFO "scull: major = %d minor = %d\n", scull_major, scull_minor);

	return 0;

fail:
	scull_cleanup_module();
	return rv;
}

MODULE_AUTHOR("Your name");
MODULE_LICENSE("GPL");

module_init(scull_init_module);		
module_exit(scull_cleanup_module);	


С удовольствием выслушаю конструктивную критику и буду ждать feedback'a.

Если вы нашли ошибки или я не правильно изложил материал, пожалуйста, укажите мне на это.
Для более быстрой реакции пишите в ЛС.

Спасибо!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/337946/


Метки:  

[Перевод] Удивительно полезный инструмент: lsof

Четверг, 14 Сентября 2017 г. 14:59 + в цитатник

Метки:  

[Перевод] Удивительно полезный инструмент: lsof

Четверг, 14 Сентября 2017 г. 14:59 + в цитатник

Метки:  

[Перевод] Удивительно полезный инструмент: lsof

Четверг, 14 Сентября 2017 г. 14:59 + в цитатник

Метки:  

[recovery mode] Как мы делали небольшую охранную систему на RPi. Часть 2

Четверг, 14 Сентября 2017 г. 14:55 + в цитатник
iBlacksus сегодня в 14:55 Разработка

Как мы делали небольшую охранную систему на RPi. Часть 2

Здравствуйте Хабражители! В предыдущей статье я рассказывал немного про железную часть проекта охранной системы и про систему на Python, которая работает с этим железом. В этой статье я продолжаю рассказ про серверную часть.

image

Охранная система будет располагаться за пределами действия wi-fi, и у нее будет только мобильный интернет с небольшой скоростью, поэтому я решил, что обмен данными нужно сделать как можно менее затратным. В данном случае самое простое решение, это использование цифровых кодов. Т.е. мы отправляем на сервер 1 и он понимает, что сработал датчик движение, 2 — датчик дыма и т.д., для каждого события свой код. В свою очередь сервер расшифровывает коды и отправляет то, что считает нужным, в виде push-уведомления в iOS приложение, уже в виде текста. При этом каждое событие записывается как в лог на стороне RPi, так и в лог сервера, этот лог так же можно посмотреть в приложении. Кстати, вся работа с сервером в Python осуществляется с помощью Requests.

Сервер написан на PHP, так же там присутствует MySQL, в ней хранится лог и текущие настройки, т.е. всего две таблицы, вот одна из них:



Все запросы принимаются с помощью обычного POST, и отдаются в JSON. Выглядит это примерно так:

print json_encode(getRequest($_POST));

Для работы серверу нужно не так много методов:

  • Получать сообщение от RPi
  • Отдавать лог
  • Устанавливать настройки
  • Отдавать настройки

Теперь по поводу настроек, я долго думал как мне реализовать управление охранной системой из приложения, и пришел к выводу, что мне вполне подойдет способ, когда настройки хранятся на сервере, и охранная система с периодичностью в минуту их обновляет. Т.е. если я хочу выключить сигнализацию, в самом плохом случае она выключится через минуту. При этом она оповестит меня о том, что выключилась, поэтому неприятного сюрприза не будет.

Так же сервер следит за тем, чтобы RPi каждую минуту сообщал, что он в сети и с ним ничего не случилось. Если этого не происходит в течение 5 минут, то сервер начинает бить тревогу. Сделана эта проверка с помощью cron'а

*/5 * * * * php /aliceserver/checkAlice.php

Он каждые 5 минут запускает простенький скриптик проверки

include_once $dir . 'defines.php';
include_once $dir . 'db.php';

$db = new DataBase;
$db->connect();

$query = "SELECT alice, UNIX_TIMESTAMP(online) online FROM " . SQLTableStatus;
$result = $db->query($query);

$alice_is_on = $result[0]['alice'];
$online = (time() - $result[0]['online'] < 5*60);

if ($alice_is_on and !$online) {
	include_once $dir . 'send_push.php';
	
	$text = "Alice offline!";
		
	$query = "INSERT INTO `" . SQLTableLog . "` (text, sender) VALUES ('$text', 1)";
	$db->query($query);
		
	send_push_message($text, AppToken, 'alice_offline.caf', $dir);
}

$db->disconnect();

Отправка push-уведомлей реализована с помощью ApnsPHP. Те из вас, кто хоть раз сталкивался с отправкой push-уведомлений на сервера Apple, наверняка знакомы с этим замечательным open-sourse решением. Для тех, кто не знает что это, могу только сказать, что на текущий момент это одна из лучших оберток, которая позволяет легко отправлять уведомления, при этом для ее настройки нужно минимум времени и усилий.

На этом рассказ про серверную часть подходит к концу, в следующий раз я расскажу как делалось iOS приложение. Всем спасибо за внимание, по возможности отвечу на все вопросы по железу и софту в комментариях.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/231963/


Метки:  

[recovery mode] Как мы делали небольшую охранную систему на RPi. Часть 2

Четверг, 14 Сентября 2017 г. 14:55 + в цитатник
iBlacksus сегодня в 14:55 Разработка

Как мы делали небольшую охранную систему на RPi. Часть 2

Здравствуйте Хабражители! В предыдущей статье я рассказывал немного про железную часть проекта охранной системы и про систему на Python, которая работает с этим железом. В этой статье я продолжаю рассказ про серверную часть.

image

Охранная система будет располагаться за пределами действия wi-fi, и у нее будет только мобильный интернет с небольшой скоростью, поэтому я решил, что обмен данными нужно сделать как можно менее затратным. В данном случае самое простое решение, это использование цифровых кодов. Т.е. мы отправляем на сервер 1 и он понимает, что сработал датчик движение, 2 — датчик дыма и т.д., для каждого события свой код. В свою очередь сервер расшифровывает коды и отправляет то, что считает нужным, в виде push-уведомления в iOS приложение, уже в виде текста. При этом каждое событие записывается как в лог на стороне RPi, так и в лог сервера, этот лог так же можно посмотреть в приложении. Кстати, вся работа с сервером в Python осуществляется с помощью Requests.

Сервер написан на PHP, так же там присутствует MySQL, в ней хранится лог и текущие настройки, т.е. всего две таблицы, вот одна из них:



Все запросы принимаются с помощью обычного POST, и отдаются в JSON. Выглядит это примерно так:

print json_encode(getRequest($_POST));

Для работы серверу нужно не так много методов:

  • Получать сообщение от RPi
  • Отдавать лог
  • Устанавливать настройки
  • Отдавать настройки

Теперь по поводу настроек, я долго думал как мне реализовать управление охранной системой из приложения, и пришел к выводу, что мне вполне подойдет способ, когда настройки хранятся на сервере, и охранная система с периодичностью в минуту их обновляет. Т.е. если я хочу выключить сигнализацию, в самом плохом случае она выключится через минуту. При этом она оповестит меня о том, что выключилась, поэтому неприятного сюрприза не будет.

Так же сервер следит за тем, чтобы RPi каждую минуту сообщал, что он в сети и с ним ничего не случилось. Если этого не происходит в течение 5 минут, то сервер начинает бить тревогу. Сделана эта проверка с помощью cron'а

*/5 * * * * php /aliceserver/checkAlice.php

Он каждые 5 минут запускает простенький скриптик проверки

include_once $dir . 'defines.php';
include_once $dir . 'db.php';

$db = new DataBase;
$db->connect();

$query = "SELECT alice, UNIX_TIMESTAMP(online) online FROM " . SQLTableStatus;
$result = $db->query($query);

$alice_is_on = $result[0]['alice'];
$online = (time() - $result[0]['online'] < 5*60);

if ($alice_is_on and !$online) {
	include_once $dir . 'send_push.php';
	
	$text = "Alice offline!";
		
	$query = "INSERT INTO `" . SQLTableLog . "` (text, sender) VALUES ('$text', 1)";
	$db->query($query);
		
	send_push_message($text, AppToken, 'alice_offline.caf', $dir);
}

$db->disconnect();

Отправка push-уведомлей реализована с помощью ApnsPHP. Те из вас, кто хоть раз сталкивался с отправкой push-уведомлений на сервера Apple, наверняка знакомы с этим замечательным open-sourse решением. Для тех, кто не знает что это, могу только сказать, что на текущий момент это одна из лучших оберток, которая позволяет легко отправлять уведомления, при этом для ее настройки нужно минимум времени и усилий.

На этом рассказ про серверную часть подходит к концу, в следующий раз я расскажу как делалось iOS приложение. Всем спасибо за внимание, по возможности отвечу на все вопросы по железу и софту в комментариях.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/231963/


Метки:  

[recovery mode] Как мы делали небольшую охранную систему на RPi. Часть 2

Четверг, 14 Сентября 2017 г. 14:55 + в цитатник
iBlacksus сегодня в 14:55 Разработка

Как мы делали небольшую охранную систему на RPi. Часть 2

Здравствуйте Хабражители! В предыдущей статье я рассказывал немного про железную часть проекта охранной системы и про систему на Python, которая работает с этим железом. В этой статье я продолжаю рассказ про серверную часть.

image

Охранная система будет располагаться за пределами действия wi-fi, и у нее будет только мобильный интернет с небольшой скоростью, поэтому я решил, что обмен данными нужно сделать как можно менее затратным. В данном случае самое простое решение, это использование цифровых кодов. Т.е. мы отправляем на сервер 1 и он понимает, что сработал датчик движение, 2 — датчик дыма и т.д., для каждого события свой код. В свою очередь сервер расшифровывает коды и отправляет то, что считает нужным, в виде push-уведомления в iOS приложение, уже в виде текста. При этом каждое событие записывается как в лог на стороне RPi, так и в лог сервера, этот лог так же можно посмотреть в приложении. Кстати, вся работа с сервером в Python осуществляется с помощью Requests.

Сервер написан на PHP, так же там присутствует MySQL, в ней хранится лог и текущие настройки, т.е. всего две таблицы, вот одна из них:



Все запросы принимаются с помощью обычного POST, и отдаются в JSON. Выглядит это примерно так:

print json_encode(getRequest($_POST));

Для работы серверу нужно не так много методов:

  • Получать сообщение от RPi
  • Отдавать лог
  • Устанавливать настройки
  • Отдавать настройки

Теперь по поводу настроек, я долго думал как мне реализовать управление охранной системой из приложения, и пришел к выводу, что мне вполне подойдет способ, когда настройки хранятся на сервере, и охранная система с периодичностью в минуту их обновляет. Т.е. если я хочу выключить сигнализацию, в самом плохом случае она выключится через минуту. При этом она оповестит меня о том, что выключилась, поэтому неприятного сюрприза не будет.

Так же сервер следит за тем, чтобы RPi каждую минуту сообщал, что он в сети и с ним ничего не случилось. Если этого не происходит в течение 5 минут, то сервер начинает бить тревогу. Сделана эта проверка с помощью cron'а

*/5 * * * * php /aliceserver/checkAlice.php

Он каждые 5 минут запускает простенький скриптик проверки

include_once $dir . 'defines.php';
include_once $dir . 'db.php';

$db = new DataBase;
$db->connect();

$query = "SELECT alice, UNIX_TIMESTAMP(online) online FROM " . SQLTableStatus;
$result = $db->query($query);

$alice_is_on = $result[0]['alice'];
$online = (time() - $result[0]['online'] < 5*60);

if ($alice_is_on and !$online) {
	include_once $dir . 'send_push.php';
	
	$text = "Alice offline!";
		
	$query = "INSERT INTO `" . SQLTableLog . "` (text, sender) VALUES ('$text', 1)";
	$db->query($query);
		
	send_push_message($text, AppToken, 'alice_offline.caf', $dir);
}

$db->disconnect();

Отправка push-уведомлей реализована с помощью ApnsPHP. Те из вас, кто хоть раз сталкивался с отправкой push-уведомлений на сервера Apple, наверняка знакомы с этим замечательным open-sourse решением. Для тех, кто не знает что это, могу только сказать, что на текущий момент это одна из лучших оберток, которая позволяет легко отправлять уведомления, при этом для ее настройки нужно минимум времени и усилий.

На этом рассказ про серверную часть подходит к концу, в следующий раз я расскажу как делалось iOS приложение. Всем спасибо за внимание, по возможности отвечу на все вопросы по железу и софту в комментариях.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/231963/


Метки:  

Ежемесячная рубрика «Читаем статьи за Вас». Август 2017

Четверг, 14 Сентября 2017 г. 14:09 + в цитатник
grisme сегодня в 14:09 Разработка

Ежемесячная рубрика «Читаем статьи за Вас». Август 2017

    image


    Привет, Хабр!
    С этого выпуска мы начинаем хорошую традицию: каждый месяц будет выходить набор рецензий на некоторые научные статьи от членов сообщества Open Data Science из канала #article_essence. Хотите получать их раньше всех — вступайте в сообщество ODS!
    Статьи выбираются либо из личного интереса, либо из-за близости к проходящим сейчас соревнованиям. Если вы хотите предложить свою статью или у вас есть какие-то пожелания — просто напишите в комментариях и мы постараемся всё учесть в дальнейшем.


    Статьи на сегодня:


    1. Fitting to Noise or Nothing At All: Machine Learning in Markets
    2. Learned in Translation: Contextualized Word Vectors
    3. Create Anime Characters with A.I. !
    4. LiveMaps: Converting Map Images into Interactive Maps
    5. Random Erasing Data Augmentation
    6. YellowFin and the Art of Momentum Tuning
    7. The Devil is in the Decoder
    8. Improving Deep Learning using Generic Data Augmentation
    9. Learning both Weights and Connections for Efficient Neural Networks
    10. Focal Loss for Dense Object Detection
    11. Borrowing Treasures from the Wealthy: Deep Transfer Learning through Selective Joint Fine-tuning
    12. Model-Agnostic Meta-Learning for Fast Adaptation of Deep Networks

    1. Fitting to Noise or Nothing At All: Machine Learning in Markets


    Оригинал статьи
    Автор: kt{at}ut{dot}ee


    Это рецензия на статью, в которой диплернингом предсказываются финансовые рынки. Автор поста (далее — ZHD) указывает на следующие очевидные глупости данной и схожих статей:


    • Притягивание за уши показателя accuracy методом взятия максимума из всех экспериментов. Медиана, наоборот, как раз соответствует случайному предсказанию.
    • Хвастовство "хорошими результатами", которые получены на инструментах, которые сейчас вообще не торгуются (т.е. у них константная цена). В целом, ZHD рекомендует сравнивать трейдинговые модели не со случайным предсказателем, а со стратегией buy and hold.
    • Авторы "симулируют" свою стратегию, предполагая отсутствие комиссий при сделках и то, что любую сделку можно выполнить по цене, равной среднему между high и low за последние пять минут. Из-за этого в симуляциях наилучшая прибыль получается у наименее ликвидных инструментов (которые на деле далеко не всегда продадутся по средней цене за пять минут, поэтому вся прибыль скорее всего "получена" за счет нечестного предположения о ликвидности). Вдобавок, авторы хвастаются только несколькими "удачными" моделями, умалчивая всё, что отсимулировалось в минус.
    • Авторы не учитывают время работы бирж (в том числе, очные сессии для сделок (trading pits)) и по мнению ZHD это неправильно.

    2. Learned in Translation: Contextualized Word Vectors


    Оригинал статьи
    Код
    Автор: kt{at}ut{dot}ee


    Трансфер лернинг в наше время — стандартный прием. Взять сетку, натренированную на породах болонок из ImageNet, и прикрутить её куда-то для распознавания типов соплей — это уже стандартная программа для всех мамкиных диплёрнеров. В контексте обработки текста, трансфер лернинг обычно менее глубокий и упирается в использование заготовленных векторов слов типа Word2Vec, GloVe итд.


    Авторы статьи предлагают углубить текстовый трансфер лернинг на один уровень следующим образом:


    • Натренируем LSTM-based seq2seq модель (вида encoder + decoder with attention over encoder hidden states) для перевода, скажем, с английского на немецкий.
    • Возьмем из неё только encoder (он будет простенького вида LSTM(Embedding(word_idxs)). Этот энкодер умеет превращать последовательность слов в последовательность LSTM hidden states. Т.к. эти hidden states взяты не случайно (их переводческая модель использует в своём аттеншене), то однозначно в них будет полезный сигнал.
    • Ну и всё, давайте теперь строить любые другие текстовые модели, которым мы на вход будем скармливать не только GloVe векторы слов, но и приклеенные к ним соответствующие LSTM hidden векторы из нашего переводческого энкодера (их мы будем называть Context Vectors, CoVe).

    Далее авторы наворачивают какую-то нетривиальную модель c biattention и maxout (видимо, завалялась с их прошлой работы) и сравнивают, как она работает в разных задачах, если ей на входе скормить случайный эмбеддинг, GloVe, GloVe+CoVe, GloVe+CoVe+CharNGramEmbeddings.


    По результатам кажется, что добавление CoVe повышает точность голого GloVe примерно на 1%. Иногда эффект меньше, иногда отрицательный, иногда добавление вместо CoVe в модель CharNGrams работает так же или даже лучше. В любом случае, совмещение GloVe+CoVe+CharNGrams работает точно лучше всех других методов.


    image


    На мой взгляд, из-за того, что авторы навернули нехилую модель с аттеншеном поверх сравниваемых типов эмбеддинга (GloVe vs CoVe), замер эффекта полезности CoVe получился излишне зашумленным и не очень убедительным. Я бы предпочел увидеть более "лабораторный" замер.


    3. Create Anime Characters with A.I. !


    Оригинал статьи
    Сайт
    Автор: kt{at}ut{dot}ee


    Существует сайт Getchu, на котором собраны "профили" анимешных героев различных японских игр с картинками-иллюстрациями. Эти картинки можно скачать.


    Для нахождения лица на картинке можно использовать некую тулзу "lbpcascade animeface". Таким образом авторы получили 42к анимешных лиц, которые они потом ручками пересмотрели и выкинули 4% плохих примеров.


    Имеется некая уже готовая CNN-модель Illustration2Vec, которая умеет распознавать на анимешных картинках свойства типа "улыбка", "цвет волос", итд. Авторы использовали её для того, чтобы отлейблить картинки и выбрали 34 интересующих их тэга.
    Авторы запихнули это всё в DRAGAN (Kodali et al, где это отличается от обычных GAN авторы не углубляются, видимо, непринципиально).
    Чтобы уметь генерировать картинки с заданными атрибутами, авторы делают как в ACGAN:


    • Кормят генератору вектор атрибутов.
    • Заставляют дискриминатор этот вектор предсказывать.
    • Дополнительно штрафуют генератор пропорционально тому, насколько дискриминатор не угадал верный класс.

    И генератор, и дискриминатор — довольно замороченные конволюционные SRResNet-ы (у генератора 16-блоковый, у дискриминатора 10-блоковый). У дискриминатора авторы убрали батчнорм-слои "since it would bring correlations within the mini-batch, which is undesired for the computation of the gradient norm." Я не очень понял эту проблему, поясните если вдруг кому ясно.
    Тренировалось всё Адамом с уменьшающимся lr начиная от 0.0002, не очень понятно как долго.
    Для вебаппа авторы сконвертировали сеть под WebDNN (https://github.com/mil-tokyo/webdnn) и поэтому генерят все картинки прямо в браузере на клиенте (!).


    4. LiveMaps: Converting Map Images into Interactive Maps


    [Оригинал статьи](http://dl.acm.org/citation.cfm?id=3080673, https://doi.org/10.1145/3077136.3080673)
    Статья — победитель Best Short Paper Awart SIGIR 2017
    Автор: zevsone


    Предложена принципиально новая система (LiveMaps) для анализа изображений карт и извлечения релевантного viewport'a.


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


    LiveMaps работает в несколько приемов. Сперва проверяется является ли изображение картой.
    Если "да", система пытается определить геолокацию этого изображения. Для определения локации используется текстовая и визуальная информация извлеченная из изображения. В итоге, система создает интерактивную карту, отображающую географическую область, вычисленную для изображения.


    Результаты оценки на датасете локаций с высоким рангом показывают, что система способна конструировать очень точные интерактивные карты, достигая при этом хорошего покрытия.


    P.S. Совсем не ожидал получить best short paper award на такой маститой конфе (121 short paper в претендентах в этом году, все industry гиганты).


    5. Random Erasing Data Augmentation


    Оригинал статьи
    Автор: egor.v.panfilov{at}gmail{dot}com


    Статья посвящена исследованию одного из простейших методов аугментации изображений — Random Erasing (по-русски, закрашиванию случайных прямоугольников).


    Аугментация параметризовалась 4-мя параметрами: (P_prob) вероятность применения к каждой картинке батча, (P_area) размер региона (area ratio), (P_aspect) соотношение сторон региона (aspect ratio), (P_value) заполняемое значением: случайные / среднее на ImageNet / 0 / 255.


    image


    Авторы провели оценку влияния данного метода аугментации на 3-х задачах: (A) object classification, (B) object detection, © person re-identification.


    (A): Использовались 6 архитектур, начиная с AlexNet, заканчивая ResNeXt. Датасет — CIFAR10/100. Оптимальное значение параметров: P_prob = 0.5, P_aspect = в широком диапазоне, но желательно не 1(квадрат), P_area = 0.02-0.4 (2-40% изображения), P_value = random или среднее на ImageNet, для 0 и 255 результаты существенно хуже. Сравнили также с другими методами и аугментации (random cropping, random flipping), и регуляризации (dropout, random noise): в порядке снижения эффективности — всё вместе, random cropping, random flipping, random erasing. Drouput и random noise данный метод уделывет "в щепки". В целом, метод не самый мощный, но при оптимальных параметрах стабильно даёт 1% точности (5.5% -> 4.5%). Также пишут, что он повышает робастность классификатора к перекрытиям объектов :you-dont-say:.


    (B): Использовалась Fast-RCNN на PASCAL VOC 2007+2012. Реализовали 3 схемы: IRE (Image-aware Random Erasing, также выбираем регион для зануления вслепую), ORE (Object-aware, зануляем только части bounding box'ов), I+ORE (и там, и там). Существенной разницы по mAP между этими методами нет. По сравнению с чистым Fast-RCNN, дают порядка 5% (67->71 на VOC07, 70->75 на VOC07+12), столько же, сколько и A-Fast-RCNN. Оптимальные параметры — P_prob = 0.5, P_area = 0.02-0.2 (2-20%), P_aspect = 0.3-3.33 (от лежачей до стоячей полоски).


    ©: Использовались ID-discim.Embedding (IDE), Triplet Net, и SVD-Net (все на базе ResNet, предобученной на ImageNet) на Market-1501 / DukeMTMC-reID / CUHK03. На всех моделях и датасетах аугментация стабильно даёт не менее 2% (до 8%) для Rank@1, и не менее 3% (до 8%) для mAP. Параметры те же, что и для (B).


    В целом, несмотря на простоту метода, исследование и статья довольно аккуратные и подробные (10 страниц), с кучей графиков и таблиц. Порадовали китайцы, ничего не скажешь.


    6. YellowFin and the Art of Momentum Tuning


    Оригинал статьи
    Дополнительный материал про momentum
    Автор: Arech


    Давно я её читал, поэтому очень-очень поверхностно: после вдумчивого курения свойств классического моментума (он же моментум Бориса Поляка), на одномерных строго выпуклых квадратичных целях чуваки вывели (или нашли у кого-то?) неравенство, связывающее коэффициенты learning rate и моментума так, чтобы они попадали в некий "робастный" регион, гарантирующий наибыстрейшее схождение алгоритма SGD. Потом показали(?), что оное утверждение в принципе может как-то выполняться и для некоторых невыпуклых функций, по крайней мере в некоторой их локальной области, которую можно более-менее апроксимировать квадратичным приближением. После чего решили запилить свой тюнер YellowFin, который на основании знания предыдущей истории изменения градиента, апроксимировал бы нужные для неравенства характеристики поверхности функции ошибки (дисперсия градиента, некая "обощённая" кривизна поверхности и расстояние до локального минимума квадратичной апроксимации текущей точки) и, с помощью этих апроксимаций, выдавал бы подходящие значения learning rate и моментума для использования в SGD.


    Также, изучив вопрос асинхронной (распределённой) тренировки сетей, чуваки предложили обобщение своего метода (Closed-loop YellowFin), которое учитывает, что реальный моментум в таких условиях оказывается больше запланированного.


    Тестировали свёрточные 110- и 164-слойные ResNet на CIFAR10 и 100 соответственно, и какие-то LSTM на PTB, TS и WSJ. Результаты интересные (от х1.18 до х2.8 ускорения относительно Адама), но, как обычно, есть вопросы к постановке эксперимента — грубый подбор коэффициентов для компетиторов, + емнип, только один прогон на каждую архитектуру, + показаны результаты только тренировочного набора… Короче, есть к чему докопаться...


    Как-то так, надеюсь, по деталям не сильно наврал.


    Я подумывал запилить его к своей либине, но крепко влип в Self-Normalizing Neural Networks (SELU+AlphaDropout), которыми занялся чуть раньше и которые оказались мне крайне полезны, поэтому пока руки не дошли. Слежу за тем тредом в Lesagne (https://github.com/Lasagne/Lasagne/issues/856 — у человека возникли проблемы с воспроизводством результатов) и вообще, надеюсь, что будет больше инфы по воспроизводству их результатов. Так что если кто попробует — делитесь чокак.


    7. The Devil is in the Decoder


    Оригинал статьи
    Автор: ternaus


    Вопрос с тем, какой UpSampling лучше для различных архитектур, где есть Decoder, теребит умы многих, особенно тех, кто борется за пятый знак после запятой в задачах сегментации, super resolution, colorization, depth, boundary detection.


    Ребята из Гугла и UCL заморочились и написали статью, где эмпирическим путем решили проверить кто лучше и найти в этом логику.


    Проверили — выяснилось, что разница есть, но логика не очень просматривается.


    Для сегментации:


    [1] Transposed Conv = Upsampling + Conv и который все яростно используют в Unet норм.
    [2] добрасывание skiped connections, то есть, трансформация SegNet => Unet железобетонно добрасывает. Это интуитивно понятно, но тут есть циферки.
    [3] Такое ощущение, что Separable Transposed, которое Transposed, но с меньшим числом параметоров для сегментации, работает лучше. Хотелось бы, чтобы народ в #proj_cars это проверил.
    [4] Хитроумный Bilinear additive Upsampling, который они предложили, на сегментации работает примерно как [3]. Но это тоже в сторону коллектива из #proj_cars проверить
    [5] Они добрасывают residual connections куда-то, что тоже в теории может что-то добавить, но куда именно — не очень понятно, и добрасывает очень неуверенно и не всегда.


    Для задач сегментации они используют resnet 50 как base и потом сверху добавляют decoder.


    Для задачи instance boundary detection они решили выбрать метрику, при которой меньше оверфитишь на алгоритм разметки + циферки побольше получаются.
    То есть, During the evaluation, predicted contour pixels within three from ground truth pixels are assumed to be correct. Что сразу ставит вопрос о том, как это все переносится на задачи, где важен каждый пиксель. (Тут вспоминается Костин трюк со спутников для поиска заборов толщиной в один пиксель и то, как народ борется за +-1 пиксель на границах в задаче про машинки)


    [6] У всех сетей, что они тренируют, у весов используется L2 регуляризация порядка 0.0002
    Карпатый, вроде, тоже в свое время говорил, что всегда так делает для более стабильной сходимости. (Это мне надо попробовать, если кто так делает и это дает что-то заметное, было бы неплохо рассказать об этом в тредике)


    Summary:
    [1] Вопрос о том, кто и когда лучше они подняли, но не ответили.
    [2] Они предложили еще один метод делать Upsampling, который работает не хуже других.
    [3] Подтвердили, что skipped connection однозначно помогают, а residual — в зависимости от фазы луны.


    Ждем месяц, о том, что скажет человеческий GridSearch на #proj_cars.


    8. Improving Deep Learning using Generic Data Augmentation


    Оригинал статьи
    Автор: egor.v.panfilov{at}gmail{dot}com


    Эпиграф: Лавры китайцев не дают покоя никому, даже на чёрном континенте. Хороших компьютеров, правда, пока не завезли, но Тернаус велел писать, "и вот они продолжают".


    Авторы попытались провести бенчмарк методов аугментации изображений на задаче классификации изображений, и выработать рекомендации по их использованию для различных случаев. В первом приближении, данные методы разделить на 2 категории: Generic (общеприменимые) и Complex (использующие информацию о домене / генеративные). В данной статье рассматриваются только Generic.


    Все эксперименты в статье проводились на Caltech-101 (101 класс, 9144 изображения) с использованием ZFNet (половина информативной части статьи о том, как лучше всего обучить ванильную ZFNet). Обучали 30 эпох, используя DL4j. Рассматриваемые методы аугментации: (1) без аугмен-ции, (2-4) геометрические: horizontal flipping, rotating (-30deg и +30deg), cropping (4 угловых кропа), (5-7) фотометрические: color jittering, edge enhacement (добавляем к изображению результат фильтра Собеля), fancy PCA (усиливаем principal компоненты на изображениях).


    Результаты: к бейзлайну (top1/top5: 48.1%/64.5%) (a) flipping даёт +1/2%, но увеличивает разброс точности, (b) rotating даёт +2%, © cropping даёт +14%, (d) color jittering +1.5/2.5%, (d-e) edge enhancement и fancy PCA по +1/2%. Т.е. среди геометрических методов впереди cropping, среди фотометрических — color jittering. В заключении авторы пишут, что существенное улучшение точности при аугментации cropping'ом может быть связано с тем, что датасет получается в 4 раза больше исходного (сбалансировать-то не судьба). Из положительного — не забыли 5-fold кросс-валидацию при оценке точности моделей. Почему были выбраны конкретно эти методы аугментации среди других (в т.ч. более популярных) узнаем, видимо, в следующей статье.


    9. Learning both Weights and Connections for Efficient Neural Networks


    Оригинал статьи
    Автор: egor.v.panfilov{at}gmail{dot}com


    Статья рассматривает проблему выского потребления ресурсов современными архитектурами DNN (в частности, CNN). Самый главный бич — это обращение к динамической памяти. Например, инференс сети с 1 млрд связей на 20Гц потребляет ~13Вт.


    Авторы предлагают метод снижения числа активных нейронов и связей в сети — prunning. Работает следующим образом: (1) обучаем сеть на полном датасете, (2) маскируем связи с весами ниже определённого уровня, (3) дообучаем оставшиеся связи на полном датасете. Шаги (2) и (3) можно и нужно повторять несколько раз, так как агрессивный (за 1 подход) prunning показывает результаты немного хуже (для AlexNet на ImageNet, например, 5x против 9x). Хитрости: использовать L2-регуляризацию весов, при дообучении уменьшать dropout, уменьшать learning rate, прунить и дообучать CONV и FC слои раздельно, выкидывать мёртвые (non-connected) нейроны по результатам шага (2).


    Эксперименты производились в Caffe с сетями Lenet-300-100, Lenet-5 на MNIST, AlexNet, VGG-16 на ImageNet. На MNIST: уменьшили число весов и FLOP'ов в 12 раз, а также обнаружили, что prunning проявляет свойство механизма attention (по краям режет больше). На ImageNet: AlexNet обучали 75 часов и дообучали 173 часа, VGG-16 прунили и дообучали 5 раз. По весам удалось сжать в 9 и 13 раз соответственно, по FLOP'ам — в 3.3 и 5 раз. Интересен профиль того, как прунятся связи: первый CONV-слой ужимается меньше чем в 2 раза, следующие CONV — в 3 и более (до 12), скрытые FC — в 10-20 раз, последний FC слой — в 4 раза.


    В заключение, авторы приводят результаты сравнения различных методов prunning'а (с L1-, L2-регуляризацией, с дообучением и без, отдельно по CONV, отдельно по FC). Вкратце, есть лень прунить, то учите сеть с L1, и половину слоёв можно просто выкинуть. Если не лень — только L2, прунить и дообучать итеративно ~5 раз. И последнее, хранение весов в sparse виде у авторов давало overhead в ~16% — не то чтобы критично, когда у вас сеть в 10 раз меньше.
    image


    10. Focal Loss for Dense Object Detection


    Оригинал статьи
    Автор: kt{at}ut{dot}ee


    Как известно, процесс поиска моделей в машинном обучении упирается в оптимизацию некой целевой функции потерь. Самая простая функция потерь — процент ошибок на тренинг-сете, но её оптимизировать сложно и результат статистически плох, поэтому на практике используют разные суррогатные лоссы: квадрат ошибки, минус логарифм вероятности, экспонента от минус скора, hinge loss и т.д. Все суррогатные лоссы являются монотонными функциями, штрафующими ошибки тем больше, чем больше величина ошибки. Любой лосс можно интерпретировать как вид распределения целевой переменной (например, квадрат ошибок соответствует гауссовому распределению).


    Авторы работы обнаружили, что суррогатный лосс вида


    loss(p, y) := -(1-p)^gamma log(p) if y == 1 else -p^gamma log(1-p)


    ещё нигде не использовался и не публиковался. Зачем нужно использовать именно такой лосс, а не ещё какой-нибудь, и каков смысл подразумеваемого распределения, авторы не знают, но им он кажется прикольным, т.к. здесь есть лишний параметр gamma, с помощью которого можно как будто бы подкручивать величину штрафа "легким" примерам. Авторы назвали эту функцию "focal loss".


    Авторы подобрали один набор данных и одну модель-нейросеть на которых, если подогнать значения параметра, в табличке результатов виден якобы некий положительный эффект от использования такого лосса вместо обычной кросс-энтропии (взвешенной по классам). На самом деле, большая часть статьи жует вопросы применения RetinaNet для детекции объектов, не сильно зависящие от выбора лосс-функции.


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


    Альтернативное мнение
    Следи за руками: чуваки взяли один из стандартных, довольно сложных датасетов, на которых меряются в детекции, взяли простенькую сеть без особых наворотов (то из чего все другие уже пробовали выжать сколько могли), приложили свой лосс и получили сходу результат single model без мультскейла и прочих трюков выше чем все остальные на этом датасете, включая все сети в одну стадию и более продвинутые двухстадийные. Чтобы убедиться, что дело в лоссе, а не чём-то ещё, они попробовали другие варианты, которые до этого были крутыми и модными техниками — балансирование кросс-энтропии, OHEM и получили результат стабильно выше на своём. Покрутили параметры своего и нашли вариант, который работает лучше всех и даже попробовали чуток объяснить почему (там есть график, где видно что гамма ниже 2 даёт довольно гладкое распределение, а больше двух уж очень резко штрафует (даже при двух там практически полка, удивительно что работает)).
    Конечно, можно было им начать сравнивать 40 вариантов сетей, с миллионом вариантов гиперпараметров и на всех известных датасетах, да ещё и кроссвалидацию 10 раз по 10 фолдов, но сколько бы это времени тогда заняло и когда была бы публикация готова и сколько бы там было разных идей?
    Тут всё просто — изменили один компонент, получили результат превосходящий SoTA на конкретном датасете. Убедились, что результат вызван изменением, а не чем-то ещё. Fin.


    Альтернативное мнение
    Наверное, стоит добавить, что если бы статья позиционировалась как представление RetinaNet, она бы смотрелась, по-моему, совсем по-другому. Она ведь и построена на самом-то деле в основном как пример применения RetinaNet. Зачем в неё внезапно вставили упор на лосс и странный заголовок, мне лично непонятно. Объективных замеров, подтверждающих высказанные про этот лосс тезисы там всё-таки нет.


    Может быть, например, RetinaNet планируется опубликовать в более серьезном формате и с другим порядком авторов, а это — просто продукт стороннего эксперимента, который тоже решили лишней публикацией оформить потому что студент ведь работал и молодец. В таком случае это опять же пример того, как высосать лишнюю статью из воздуха.


    В любом случае, я для себя никак не могу вынести из этой статьи обещанный в заглавии и тексте тезис "вкручивайте такой лосс везде и будет вам счастье".


    Тезис "RetinaNet хорошо работает на COCO (с любым лоссом, причем!)" я, при этом, вынести вполне могу.


    11. Borrowing Treasures from the Wealthy: Deep Transfer Learning through Selective Joint Fine-tuning


    Оригинал статьи
    Код
    Автор: movchan74


    image


    Авторы сосредоточились на проблеме классификации изображений с небольшим датасетом. Типичный подход в таком случае следующий: взять предобученную CNN на ImageNet и дообучить на своем датасете (или даже дообучить только классификационные полносвязный слои). Но при этом сеть быстро переобучается и достигает не таких значений точности, каких хотелось бы. Авторы предлагают использовать не только целевой датасет (target dataset, мало данных, далее буду называть датасет T), но и дополнительный исходный датесет (source dataset, далее буду называть датасет S) с большим количеством изображений (обычно ImageNet) и обучать мультитаск на двух датасетах одновременно (делаем после CNN две головы по одной на датасет).


    Но как выяснили авторы, использование всего датасета S для обучения не очень хорошая идея, лучше использовать некоторое подмножество датасета S. И далее в статье предлагаются различные способы поиска оптимального подмножества.


    Получаем следующий фреймворк:


    1. Берем два датасета: S и T. T — датасет с малым количеством примеров, для которого нам необходимо получить классификатор, S — большой вспомогательный датасет (обычно ImageNet).
    2. Выбираем подмножество изображений из датасета S, такое, что изображения из подмножества близки к изображениям из целевого датасета T. Как именно выбирать близкие рассмотрим далее.
    3. Учим мультитаск сеть на датасете T и выбранном подмножесте S.

    Рассмотрим как предлагается выбирать подмножество датасета S. Авторы предлагают для каждого семпла из датасета T находить некоторое количество соседей из S и учиться только на них. Близость определяется как расстояние между гистограммами низкоуровневых фильтров AlexNet или фильтров Габора. Гистограммы берутся, чтобы не учитывать пространственную составляющую.


    Объяснение, почему берутся низкоуровневые фильтры, приводится следующее:


    1. Получается лучше обучить низкоуровневые сверточные слои за счет большего количества данных, а качество этих низкоуровневых фич определяет качество фич более высоких уровней.
    2. Поиск похожих изображений по низкоуровневым фильтрам позволяет находить намного больше семплов для тренировки, т.к. семантика почти не учитывается.
      Мне, если честно, эти объяснения не очень нравятся, но в статье так. Может я конечно что-то не понял или понял не так. Это все описано на 2 странице после слов "The motivation behind selecting images according to their low-level characteristics is two fold".

    Еще из особенностей поиска близких изображений:


    1. Гистограммы строятся так, чтобы в среднем по всему датасету в один бин попадало примерно одинаковое количество.
    2. Расстояние между гистограммами считается с помошью KL-дивергенции.

    Авторы попробовали разные сверточные слои AlexNet и фильтры Габора для поиска близких семплов, лучше всего получилось если использовать 1+2 сверточные слои из AlexNet.


    Также авторы предложили итеративный способ подбора количества похожих семплов из датасета S для каджого семпла из T. Изначально берем некоторое заданое число ближайших соседей для каждого отдельного семпла из T. Затем прогоняем обучение, и если ошибка для семпла большая, то увеличиваем количество ближайших соседей для этого семпла. Как именно производится увеличение ближайших соседей понятно из уравнения 6.


    Из особенностей обучения. При формировании батча случайно выбираем семплы из датасета T, и для каждого выбранного семпла берем одного из его ближайших соседей.


    Проведены эксперименты на следующих датасетах: Stanford Dogs 120, Oxford Flowers 102, Caltech 256, MIT Indoor 67. На всех датасетах получены SOTA результаты. Получилось поднять точность классфикации от 2 до 10% в зависимости от датасета.


    12. Model-Agnostic Meta-Learning for Fast Adaptation of Deep Networks


    Оригинал статьи
    Код
    Автор: repyevsky{at}gmail{dot}com
    Статья про мета-обучение: авторы хотят научить модель объединять свой предыдущий опыт с малым количеством новой информации для решения новых заданий из некоторого общего класса.


    Чтобы было понятно чего хотят добиться авторы, расскажу как оценивается модель.


    В качество бенчмарка для классификации используются два датасета: Omniglot и miniImagenet. В первом рукописные буквы из нескольких алфавитов — всего около 1600 классов, 20 примеров на класс. Во втором 100 классов из Imagenet — по 600 картинок на класс. Ещё есть раздел про RL, но его я не смотрел.


    Перед обучением все классы разбиваются на непересекающиеся множества train, validation и test. Для проверки, из test-классов (которые модель не видела при обучении) выбирается, например, 5 случайных классов (5-way learning). Для каждого из выбранных классов сэмплится несколько примеров, лейблы кодируются one-hot вектором длины 5. Дальше примеры для каждого класса делятся на две части A и B. Примеры из A показываются модели с ответами, а примеры из B используются для проверки точности классификации. Так формируется задание. Авторы смотрят на accuracy.


    Таким образом, нужно научить модель адаптироваться к новому заданию (новому набору классов) за несколько итераций/новых примеров.


    В отличии от предыдущих работ, где для этого пытались использовать RNN или feature embedding с непараметрическими методами на тесте (вроде k-ближайших соседей), авторы предлагают подход, позволяющий настроить параметры любой стандартной модели, если она обучается градиентными методами.


    Ключевая идея: обновлять веса модели так, чтобы она давала лучший результат на новых заданиях.
    Интуиция: внутри модели получатся универсальные для всех классов датасета представления входных данных, по которым модель сможет быстро подстроиться под новое задание.


    Суть в следующем. Пусть мы хотим, чтобы наша модель F(x, p) обучалась новому заданию за 1 итерацию (1-shot learning). Тогда для обучения нужно из тренировочных классов подготовить такие же задания как на тесте. Дальше на примерах из части A мы считаем loss, его градиент и делаем одну итерацию обучения — в итоге получаем промежуточные обновленные веса p' = p - a*grad и новую версию модели — F(x, p'). Считаем loss для F(x, p') на B и минимизируем его относительно исходных весов p. Получаем настоящие новые веса — конец итерации. Когда считается градиент от градиента :xzibit:, появляются вторые производные.


    На самом деле, генерируется сразу несколько заданий, объединеных в metabatch. Для каждого находится свой p' и считается свой loss. Потом все эти loss суммируются в total_loss, который уже минимизируется относительно p.


    Авторы применили свой подход к базовым моделям из предыдущих работ (маленькие сверточная и полносвязная сети) и получили SOTA на обоих датасетах.


    При этом, итоговая модель получается без дополнительных параметров для мета-обучения. Зато используется большое количество вычислений, в том числе из-за вторых производных. Авторы пробовали отбросить вторые производные на miniImagenet. При этом accuracy осталась почти такой же, а вычисления ускорились на 33%. Предположительно это связано с тем, что ReLU — кусочно-линейная функция и вторая производная у неё почти всегда нулевая.


    Код авторов на Tensorflow: https://github.com/cbfinn/maml. Там внутренний градиентый шаг делается вручную, а внешний с помощью AdamOptimizer.


    За редактуру спасибо yuli_semenova.

    Original source: habrahabr.ru (comments, light).

    https://habrahabr.ru/post/336624/


    Ежемесячная рубрика «Читаем статьи за Вас». Август 2017

    Четверг, 14 Сентября 2017 г. 14:09 + в цитатник
    grisme сегодня в 14:09 Разработка

    Ежемесячная рубрика «Читаем статьи за Вас». Август 2017

      image


      Привет, Хабр!
      С этого выпуска мы начинаем хорошую традицию: каждый месяц будет выходить набор рецензий на некоторые научные статьи от членов сообщества Open Data Science из канала #article_essence. Хотите получать их раньше всех — вступайте в сообщество ODS!
      Статьи выбираются либо из личного интереса, либо из-за близости к проходящим сейчас соревнованиям. Если вы хотите предложить свою статью или у вас есть какие-то пожелания — просто напишите в комментариях и мы постараемся всё учесть в дальнейшем.


      Статьи на сегодня:


      1. Fitting to Noise or Nothing At All: Machine Learning in Markets
      2. Learned in Translation: Contextualized Word Vectors
      3. Create Anime Characters with A.I. !
      4. LiveMaps: Converting Map Images into Interactive Maps
      5. Random Erasing Data Augmentation
      6. YellowFin and the Art of Momentum Tuning
      7. The Devil is in the Decoder
      8. Improving Deep Learning using Generic Data Augmentation
      9. Learning both Weights and Connections for Efficient Neural Networks
      10. Focal Loss for Dense Object Detection
      11. Borrowing Treasures from the Wealthy: Deep Transfer Learning through Selective Joint Fine-tuning
      12. Model-Agnostic Meta-Learning for Fast Adaptation of Deep Networks

      1. Fitting to Noise or Nothing At All: Machine Learning in Markets


      Оригинал статьи
      Автор: kt{at}ut{dot}ee


      Это рецензия на статью, в которой диплернингом предсказываются финансовые рынки. Автор поста (далее — ZHD) указывает на следующие очевидные глупости данной и схожих статей:


      • Притягивание за уши показателя accuracy методом взятия максимума из всех экспериментов. Медиана, наоборот, как раз соответствует случайному предсказанию.
      • Хвастовство "хорошими результатами", которые получены на инструментах, которые сейчас вообще не торгуются (т.е. у них константная цена). В целом, ZHD рекомендует сравнивать трейдинговые модели не со случайным предсказателем, а со стратегией buy and hold.
      • Авторы "симулируют" свою стратегию, предполагая отсутствие комиссий при сделках и то, что любую сделку можно выполнить по цене, равной среднему между high и low за последние пять минут. Из-за этого в симуляциях наилучшая прибыль получается у наименее ликвидных инструментов (которые на деле далеко не всегда продадутся по средней цене за пять минут, поэтому вся прибыль скорее всего "получена" за счет нечестного предположения о ликвидности). Вдобавок, авторы хвастаются только несколькими "удачными" моделями, умалчивая всё, что отсимулировалось в минус.
      • Авторы не учитывают время работы бирж (в том числе, очные сессии для сделок (trading pits)) и по мнению ZHD это неправильно.

      2. Learned in Translation: Contextualized Word Vectors


      Оригинал статьи
      Код
      Автор: kt{at}ut{dot}ee


      Трансфер лернинг в наше время — стандартный прием. Взять сетку, натренированную на породах болонок из ImageNet, и прикрутить её куда-то для распознавания типов соплей — это уже стандартная программа для всех мамкиных диплёрнеров. В контексте обработки текста, трансфер лернинг обычно менее глубокий и упирается в использование заготовленных векторов слов типа Word2Vec, GloVe итд.


      Авторы статьи предлагают углубить текстовый трансфер лернинг на один уровень следующим образом:


      • Натренируем LSTM-based seq2seq модель (вида encoder + decoder with attention over encoder hidden states) для перевода, скажем, с английского на немецкий.
      • Возьмем из неё только encoder (он будет простенького вида LSTM(Embedding(word_idxs)). Этот энкодер умеет превращать последовательность слов в последовательность LSTM hidden states. Т.к. эти hidden states взяты не случайно (их переводческая модель использует в своём аттеншене), то однозначно в них будет полезный сигнал.
      • Ну и всё, давайте теперь строить любые другие текстовые модели, которым мы на вход будем скармливать не только GloVe векторы слов, но и приклеенные к ним соответствующие LSTM hidden векторы из нашего переводческого энкодера (их мы будем называть Context Vectors, CoVe).

      Далее авторы наворачивают какую-то нетривиальную модель c biattention и maxout (видимо, завалялась с их прошлой работы) и сравнивают, как она работает в разных задачах, если ей на входе скормить случайный эмбеддинг, GloVe, GloVe+CoVe, GloVe+CoVe+CharNGramEmbeddings.


      По результатам кажется, что добавление CoVe повышает точность голого GloVe примерно на 1%. Иногда эффект меньше, иногда отрицательный, иногда добавление вместо CoVe в модель CharNGrams работает так же или даже лучше. В любом случае, совмещение GloVe+CoVe+CharNGrams работает точно лучше всех других методов.


      image


      На мой взгляд, из-за того, что авторы навернули нехилую модель с аттеншеном поверх сравниваемых типов эмбеддинга (GloVe vs CoVe), замер эффекта полезности CoVe получился излишне зашумленным и не очень убедительным. Я бы предпочел увидеть более "лабораторный" замер.


      3. Create Anime Characters with A.I. !


      Оригинал статьи
      Сайт
      Автор: kt{at}ut{dot}ee


      Существует сайт Getchu, на котором собраны "профили" анимешных героев различных японских игр с картинками-иллюстрациями. Эти картинки можно скачать.


      Для нахождения лица на картинке можно использовать некую тулзу "lbpcascade animeface". Таким образом авторы получили 42к анимешных лиц, которые они потом ручками пересмотрели и выкинули 4% плохих примеров.


      Имеется некая уже готовая CNN-модель Illustration2Vec, которая умеет распознавать на анимешных картинках свойства типа "улыбка", "цвет волос", итд. Авторы использовали её для того, чтобы отлейблить картинки и выбрали 34 интересующих их тэга.
      Авторы запихнули это всё в DRAGAN (Kodali et al, где это отличается от обычных GAN авторы не углубляются, видимо, непринципиально).
      Чтобы уметь генерировать картинки с заданными атрибутами, авторы делают как в ACGAN:


      • Кормят генератору вектор атрибутов.
      • Заставляют дискриминатор этот вектор предсказывать.
      • Дополнительно штрафуют генератор пропорционально тому, насколько дискриминатор не угадал верный класс.

      И генератор, и дискриминатор — довольно замороченные конволюционные SRResNet-ы (у генератора 16-блоковый, у дискриминатора 10-блоковый). У дискриминатора авторы убрали батчнорм-слои "since it would bring correlations within the mini-batch, which is undesired for the computation of the gradient norm." Я не очень понял эту проблему, поясните если вдруг кому ясно.
      Тренировалось всё Адамом с уменьшающимся lr начиная от 0.0002, не очень понятно как долго.
      Для вебаппа авторы сконвертировали сеть под WebDNN (https://github.com/mil-tokyo/webdnn) и поэтому генерят все картинки прямо в браузере на клиенте (!).


      4. LiveMaps: Converting Map Images into Interactive Maps


      [Оригинал статьи](http://dl.acm.org/citation.cfm?id=3080673, https://doi.org/10.1145/3077136.3080673)
      Статья — победитель Best Short Paper Awart SIGIR 2017
      Автор: zevsone


      Предложена принципиально новая система (LiveMaps) для анализа изображений карт и извлечения релевантного viewport'a.


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


      LiveMaps работает в несколько приемов. Сперва проверяется является ли изображение картой.
      Если "да", система пытается определить геолокацию этого изображения. Для определения локации используется текстовая и визуальная информация извлеченная из изображения. В итоге, система создает интерактивную карту, отображающую географическую область, вычисленную для изображения.


      Результаты оценки на датасете локаций с высоким рангом показывают, что система способна конструировать очень точные интерактивные карты, достигая при этом хорошего покрытия.


      P.S. Совсем не ожидал получить best short paper award на такой маститой конфе (121 short paper в претендентах в этом году, все industry гиганты).


      5. Random Erasing Data Augmentation


      Оригинал статьи
      Автор: egor.v.panfilov{at}gmail{dot}com


      Статья посвящена исследованию одного из простейших методов аугментации изображений — Random Erasing (по-русски, закрашиванию случайных прямоугольников).


      Аугментация параметризовалась 4-мя параметрами: (P_prob) вероятность применения к каждой картинке батча, (P_area) размер региона (area ratio), (P_aspect) соотношение сторон региона (aspect ratio), (P_value) заполняемое значением: случайные / среднее на ImageNet / 0 / 255.


      image


      Авторы провели оценку влияния данного метода аугментации на 3-х задачах: (A) object classification, (B) object detection, © person re-identification.


      (A): Использовались 6 архитектур, начиная с AlexNet, заканчивая ResNeXt. Датасет — CIFAR10/100. Оптимальное значение параметров: P_prob = 0.5, P_aspect = в широком диапазоне, но желательно не 1(квадрат), P_area = 0.02-0.4 (2-40% изображения), P_value = random или среднее на ImageNet, для 0 и 255 результаты существенно хуже. Сравнили также с другими методами и аугментации (random cropping, random flipping), и регуляризации (dropout, random noise): в порядке снижения эффективности — всё вместе, random cropping, random flipping, random erasing. Drouput и random noise данный метод уделывет "в щепки". В целом, метод не самый мощный, но при оптимальных параметрах стабильно даёт 1% точности (5.5% -> 4.5%). Также пишут, что он повышает робастность классификатора к перекрытиям объектов :you-dont-say:.


      (B): Использовалась Fast-RCNN на PASCAL VOC 2007+2012. Реализовали 3 схемы: IRE (Image-aware Random Erasing, также выбираем регион для зануления вслепую), ORE (Object-aware, зануляем только части bounding box'ов), I+ORE (и там, и там). Существенной разницы по mAP между этими методами нет. По сравнению с чистым Fast-RCNN, дают порядка 5% (67->71 на VOC07, 70->75 на VOC07+12), столько же, сколько и A-Fast-RCNN. Оптимальные параметры — P_prob = 0.5, P_area = 0.02-0.2 (2-20%), P_aspect = 0.3-3.33 (от лежачей до стоячей полоски).


      ©: Использовались ID-discim.Embedding (IDE), Triplet Net, и SVD-Net (все на базе ResNet, предобученной на ImageNet) на Market-1501 / DukeMTMC-reID / CUHK03. На всех моделях и датасетах аугментация стабильно даёт не менее 2% (до 8%) для Rank@1, и не менее 3% (до 8%) для mAP. Параметры те же, что и для (B).


      В целом, несмотря на простоту метода, исследование и статья довольно аккуратные и подробные (10 страниц), с кучей графиков и таблиц. Порадовали китайцы, ничего не скажешь.


      6. YellowFin and the Art of Momentum Tuning


      Оригинал статьи
      Дополнительный материал про momentum
      Автор: Arech


      Давно я её читал, поэтому очень-очень поверхностно: после вдумчивого курения свойств классического моментума (он же моментум Бориса Поляка), на одномерных строго выпуклых квадратичных целях чуваки вывели (или нашли у кого-то?) неравенство, связывающее коэффициенты learning rate и моментума так, чтобы они попадали в некий "робастный" регион, гарантирующий наибыстрейшее схождение алгоритма SGD. Потом показали(?), что оное утверждение в принципе может как-то выполняться и для некоторых невыпуклых функций, по крайней мере в некоторой их локальной области, которую можно более-менее апроксимировать квадратичным приближением. После чего решили запилить свой тюнер YellowFin, который на основании знания предыдущей истории изменения градиента, апроксимировал бы нужные для неравенства характеристики поверхности функции ошибки (дисперсия градиента, некая "обощённая" кривизна поверхности и расстояние до локального минимума квадратичной апроксимации текущей точки) и, с помощью этих апроксимаций, выдавал бы подходящие значения learning rate и моментума для использования в SGD.


      Также, изучив вопрос асинхронной (распределённой) тренировки сетей, чуваки предложили обобщение своего метода (Closed-loop YellowFin), которое учитывает, что реальный моментум в таких условиях оказывается больше запланированного.


      Тестировали свёрточные 110- и 164-слойные ResNet на CIFAR10 и 100 соответственно, и какие-то LSTM на PTB, TS и WSJ. Результаты интересные (от х1.18 до х2.8 ускорения относительно Адама), но, как обычно, есть вопросы к постановке эксперимента — грубый подбор коэффициентов для компетиторов, + емнип, только один прогон на каждую архитектуру, + показаны результаты только тренировочного набора… Короче, есть к чему докопаться...


      Как-то так, надеюсь, по деталям не сильно наврал.


      Я подумывал запилить его к своей либине, но крепко влип в Self-Normalizing Neural Networks (SELU+AlphaDropout), которыми занялся чуть раньше и которые оказались мне крайне полезны, поэтому пока руки не дошли. Слежу за тем тредом в Lesagne (https://github.com/Lasagne/Lasagne/issues/856 — у человека возникли проблемы с воспроизводством результатов) и вообще, надеюсь, что будет больше инфы по воспроизводству их результатов. Так что если кто попробует — делитесь чокак.


      7. The Devil is in the Decoder


      Оригинал статьи
      Автор: ternaus


      Вопрос с тем, какой UpSampling лучше для различных архитектур, где есть Decoder, теребит умы многих, особенно тех, кто борется за пятый знак после запятой в задачах сегментации, super resolution, colorization, depth, boundary detection.


      Ребята из Гугла и UCL заморочились и написали статью, где эмпирическим путем решили проверить кто лучше и найти в этом логику.


      Проверили — выяснилось, что разница есть, но логика не очень просматривается.


      Для сегментации:


      [1] Transposed Conv = Upsampling + Conv и который все яростно используют в Unet норм.
      [2] добрасывание skiped connections, то есть, трансформация SegNet => Unet железобетонно добрасывает. Это интуитивно понятно, но тут есть циферки.
      [3] Такое ощущение, что Separable Transposed, которое Transposed, но с меньшим числом параметоров для сегментации, работает лучше. Хотелось бы, чтобы народ в #proj_cars это проверил.
      [4] Хитроумный Bilinear additive Upsampling, который они предложили, на сегментации работает примерно как [3]. Но это тоже в сторону коллектива из #proj_cars проверить
      [5] Они добрасывают residual connections куда-то, что тоже в теории может что-то добавить, но куда именно — не очень понятно, и добрасывает очень неуверенно и не всегда.


      Для задач сегментации они используют resnet 50 как base и потом сверху добавляют decoder.


      Для задачи instance boundary detection они решили выбрать метрику, при которой меньше оверфитишь на алгоритм разметки + циферки побольше получаются.
      То есть, During the evaluation, predicted contour pixels within three from ground truth pixels are assumed to be correct. Что сразу ставит вопрос о том, как это все переносится на задачи, где важен каждый пиксель. (Тут вспоминается Костин трюк со спутников для поиска заборов толщиной в один пиксель и то, как народ борется за +-1 пиксель на границах в задаче про машинки)


      [6] У всех сетей, что они тренируют, у весов используется L2 регуляризация порядка 0.0002
      Карпатый, вроде, тоже в свое время говорил, что всегда так делает для более стабильной сходимости. (Это мне надо попробовать, если кто так делает и это дает что-то заметное, было бы неплохо рассказать об этом в тредике)


      Summary:
      [1] Вопрос о том, кто и когда лучше они подняли, но не ответили.
      [2] Они предложили еще один метод делать Upsampling, который работает не хуже других.
      [3] Подтвердили, что skipped connection однозначно помогают, а residual — в зависимости от фазы луны.


      Ждем месяц, о том, что скажет человеческий GridSearch на #proj_cars.


      8. Improving Deep Learning using Generic Data Augmentation


      Оригинал статьи
      Автор: egor.v.panfilov{at}gmail{dot}com


      Эпиграф: Лавры китайцев не дают покоя никому, даже на чёрном континенте. Хороших компьютеров, правда, пока не завезли, но Тернаус велел писать, "и вот они продолжают".


      Авторы попытались провести бенчмарк методов аугментации изображений на задаче классификации изображений, и выработать рекомендации по их использованию для различных случаев. В первом приближении, данные методы разделить на 2 категории: Generic (общеприменимые) и Complex (использующие информацию о домене / генеративные). В данной статье рассматриваются только Generic.


      Все эксперименты в статье проводились на Caltech-101 (101 класс, 9144 изображения) с использованием ZFNet (половина информативной части статьи о том, как лучше всего обучить ванильную ZFNet). Обучали 30 эпох, используя DL4j. Рассматриваемые методы аугментации: (1) без аугмен-ции, (2-4) геометрические: horizontal flipping, rotating (-30deg и +30deg), cropping (4 угловых кропа), (5-7) фотометрические: color jittering, edge enhacement (добавляем к изображению результат фильтра Собеля), fancy PCA (усиливаем principal компоненты на изображениях).


      Результаты: к бейзлайну (top1/top5: 48.1%/64.5%) (a) flipping даёт +1/2%, но увеличивает разброс точности, (b) rotating даёт +2%, © cropping даёт +14%, (d) color jittering +1.5/2.5%, (d-e) edge enhancement и fancy PCA по +1/2%. Т.е. среди геометрических методов впереди cropping, среди фотометрических — color jittering. В заключении авторы пишут, что существенное улучшение точности при аугментации cropping'ом может быть связано с тем, что датасет получается в 4 раза больше исходного (сбалансировать-то не судьба). Из положительного — не забыли 5-fold кросс-валидацию при оценке точности моделей. Почему были выбраны конкретно эти методы аугментации среди других (в т.ч. более популярных) узнаем, видимо, в следующей статье.


      9. Learning both Weights and Connections for Efficient Neural Networks


      Оригинал статьи
      Автор: egor.v.panfilov{at}gmail{dot}com


      Статья рассматривает проблему выского потребления ресурсов современными архитектурами DNN (в частности, CNN). Самый главный бич — это обращение к динамической памяти. Например, инференс сети с 1 млрд связей на 20Гц потребляет ~13Вт.


      Авторы предлагают метод снижения числа активных нейронов и связей в сети — prunning. Работает следующим образом: (1) обучаем сеть на полном датасете, (2) маскируем связи с весами ниже определённого уровня, (3) дообучаем оставшиеся связи на полном датасете. Шаги (2) и (3) можно и нужно повторять несколько раз, так как агрессивный (за 1 подход) prunning показывает результаты немного хуже (для AlexNet на ImageNet, например, 5x против 9x). Хитрости: использовать L2-регуляризацию весов, при дообучении уменьшать dropout, уменьшать learning rate, прунить и дообучать CONV и FC слои раздельно, выкидывать мёртвые (non-connected) нейроны по результатам шага (2).


      Эксперименты производились в Caffe с сетями Lenet-300-100, Lenet-5 на MNIST, AlexNet, VGG-16 на ImageNet. На MNIST: уменьшили число весов и FLOP'ов в 12 раз, а также обнаружили, что prunning проявляет свойство механизма attention (по краям режет больше). На ImageNet: AlexNet обучали 75 часов и дообучали 173 часа, VGG-16 прунили и дообучали 5 раз. По весам удалось сжать в 9 и 13 раз соответственно, по FLOP'ам — в 3.3 и 5 раз. Интересен профиль того, как прунятся связи: первый CONV-слой ужимается меньше чем в 2 раза, следующие CONV — в 3 и более (до 12), скрытые FC — в 10-20 раз, последний FC слой — в 4 раза.


      В заключение, авторы приводят результаты сравнения различных методов prunning'а (с L1-, L2-регуляризацией, с дообучением и без, отдельно по CONV, отдельно по FC). Вкратце, есть лень прунить, то учите сеть с L1, и половину слоёв можно просто выкинуть. Если не лень — только L2, прунить и дообучать итеративно ~5 раз. И последнее, хранение весов в sparse виде у авторов давало overhead в ~16% — не то чтобы критично, когда у вас сеть в 10 раз меньше.
      image


      10. Focal Loss for Dense Object Detection


      Оригинал статьи
      Автор: kt{at}ut{dot}ee


      Как известно, процесс поиска моделей в машинном обучении упирается в оптимизацию некой целевой функции потерь. Самая простая функция потерь — процент ошибок на тренинг-сете, но её оптимизировать сложно и результат статистически плох, поэтому на практике используют разные суррогатные лоссы: квадрат ошибки, минус логарифм вероятности, экспонента от минус скора, hinge loss и т.д. Все суррогатные лоссы являются монотонными функциями, штрафующими ошибки тем больше, чем больше величина ошибки. Любой лосс можно интерпретировать как вид распределения целевой переменной (например, квадрат ошибок соответствует гауссовому распределению).


      Авторы работы обнаружили, что суррогатный лосс вида


      loss(p, y) := -(1-p)^gamma log(p) if y == 1 else -p^gamma log(1-p)


      ещё нигде не использовался и не публиковался. Зачем нужно использовать именно такой лосс, а не ещё какой-нибудь, и каков смысл подразумеваемого распределения, авторы не знают, но им он кажется прикольным, т.к. здесь есть лишний параметр gamma, с помощью которого можно как будто бы подкручивать величину штрафа "легким" примерам. Авторы назвали эту функцию "focal loss".


      Авторы подобрали один набор данных и одну модель-нейросеть на которых, если подогнать значения параметра, в табличке результатов виден якобы некий положительный эффект от использования такого лосса вместо обычной кросс-энтропии (взвешенной по классам). На самом деле, большая часть статьи жует вопросы применения RetinaNet для детекции объектов, не сильно зависящие от выбора лосс-функции.


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


      Альтернативное мнение
      Следи за руками: чуваки взяли один из стандартных, довольно сложных датасетов, на которых меряются в детекции, взяли простенькую сеть без особых наворотов (то из чего все другие уже пробовали выжать сколько могли), приложили свой лосс и получили сходу результат single model без мультскейла и прочих трюков выше чем все остальные на этом датасете, включая все сети в одну стадию и более продвинутые двухстадийные. Чтобы убедиться, что дело в лоссе, а не чём-то ещё, они попробовали другие варианты, которые до этого были крутыми и модными техниками — балансирование кросс-энтропии, OHEM и получили результат стабильно выше на своём. Покрутили параметры своего и нашли вариант, который работает лучше всех и даже попробовали чуток объяснить почему (там есть график, где видно что гамма ниже 2 даёт довольно гладкое распределение, а больше двух уж очень резко штрафует (даже при двух там практически полка, удивительно что работает)).
      Конечно, можно было им начать сравнивать 40 вариантов сетей, с миллионом вариантов гиперпараметров и на всех известных датасетах, да ещё и кроссвалидацию 10 раз по 10 фолдов, но сколько бы это времени тогда заняло и когда была бы публикация готова и сколько бы там было разных идей?
      Тут всё просто — изменили один компонент, получили результат превосходящий SoTA на конкретном датасете. Убедились, что результат вызван изменением, а не чем-то ещё. Fin.


      Альтернативное мнение
      Наверное, стоит добавить, что если бы статья позиционировалась как представление RetinaNet, она бы смотрелась, по-моему, совсем по-другому. Она ведь и построена на самом-то деле в основном как пример применения RetinaNet. Зачем в неё внезапно вставили упор на лосс и странный заголовок, мне лично непонятно. Объективных замеров, подтверждающих высказанные про этот лосс тезисы там всё-таки нет.


      Может быть, например, RetinaNet планируется опубликовать в более серьезном формате и с другим порядком авторов, а это — просто продукт стороннего эксперимента, который тоже решили лишней публикацией оформить потому что студент ведь работал и молодец. В таком случае это опять же пример того, как высосать лишнюю статью из воздуха.


      В любом случае, я для себя никак не могу вынести из этой статьи обещанный в заглавии и тексте тезис "вкручивайте такой лосс везде и будет вам счастье".


      Тезис "RetinaNet хорошо работает на COCO (с любым лоссом, причем!)" я, при этом, вынести вполне могу.


      11. Borrowing Treasures from the Wealthy: Deep Transfer Learning through Selective Joint Fine-tuning


      Оригинал статьи
      Код
      Автор: movchan74


      image


      Авторы сосредоточились на проблеме классификации изображений с небольшим датасетом. Типичный подход в таком случае следующий: взять предобученную CNN на ImageNet и дообучить на своем датасете (или даже дообучить только классификационные полносвязный слои). Но при этом сеть быстро переобучается и достигает не таких значений точности, каких хотелось бы. Авторы предлагают использовать не только целевой датасет (target dataset, мало данных, далее буду называть датасет T), но и дополнительный исходный датесет (source dataset, далее буду называть датасет S) с большим количеством изображений (обычно ImageNet) и обучать мультитаск на двух датасетах одновременно (делаем после CNN две головы по одной на датасет).


      Но как выяснили авторы, использование всего датасета S для обучения не очень хорошая идея, лучше использовать некоторое подмножество датасета S. И далее в статье предлагаются различные способы поиска оптимального подмножества.


      Получаем следующий фреймворк:


      1. Берем два датасета: S и T. T — датасет с малым количеством примеров, для которого нам необходимо получить классификатор, S — большой вспомогательный датасет (обычно ImageNet).
      2. Выбираем подмножество изображений из датасета S, такое, что изображения из подмножества близки к изображениям из целевого датасета T. Как именно выбирать близкие рассмотрим далее.
      3. Учим мультитаск сеть на датасете T и выбранном подмножесте S.

      Рассмотрим как предлагается выбирать подмножество датасета S. Авторы предлагают для каждого семпла из датасета T находить некоторое количество соседей из S и учиться только на них. Близость определяется как расстояние между гистограммами низкоуровневых фильтров AlexNet или фильтров Габора. Гистограммы берутся, чтобы не учитывать пространственную составляющую.


      Объяснение, почему берутся низкоуровневые фильтры, приводится следующее:


      1. Получается лучше обучить низкоуровневые сверточные слои за счет большего количества данных, а качество этих низкоуровневых фич определяет качество фич более высоких уровней.
      2. Поиск похожих изображений по низкоуровневым фильтрам позволяет находить намного больше семплов для тренировки, т.к. семантика почти не учитывается.
        Мне, если честно, эти объяснения не очень нравятся, но в статье так. Может я конечно что-то не понял или понял не так. Это все описано на 2 странице после слов "The motivation behind selecting images according to their low-level characteristics is two fold".

      Еще из особенностей поиска близких изображений:


      1. Гистограммы строятся так, чтобы в среднем по всему датасету в один бин попадало примерно одинаковое количество.
      2. Расстояние между гистограммами считается с помошью KL-дивергенции.

      Авторы попробовали разные сверточные слои AlexNet и фильтры Габора для поиска близких семплов, лучше всего получилось если использовать 1+2 сверточные слои из AlexNet.


      Также авторы предложили итеративный способ подбора количества похожих семплов из датасета S для каджого семпла из T. Изначально берем некоторое заданое число ближайших соседей для каждого отдельного семпла из T. Затем прогоняем обучение, и если ошибка для семпла большая, то увеличиваем количество ближайших соседей для этого семпла. Как именно производится увеличение ближайших соседей понятно из уравнения 6.


      Из особенностей обучения. При формировании батча случайно выбираем семплы из датасета T, и для каждого выбранного семпла берем одного из его ближайших соседей.


      Проведены эксперименты на следующих датасетах: Stanford Dogs 120, Oxford Flowers 102, Caltech 256, MIT Indoor 67. На всех датасетах получены SOTA результаты. Получилось поднять точность классфикации от 2 до 10% в зависимости от датасета.


      12. Model-Agnostic Meta-Learning for Fast Adaptation of Deep Networks


      Оригинал статьи
      Код
      Автор: repyevsky{at}gmail{dot}com
      Статья про мета-обучение: авторы хотят научить модель объединять свой предыдущий опыт с малым количеством новой информации для решения новых заданий из некоторого общего класса.


      Чтобы было понятно чего хотят добиться авторы, расскажу как оценивается модель.


      В качество бенчмарка для классификации используются два датасета: Omniglot и miniImagenet. В первом рукописные буквы из нескольких алфавитов — всего около 1600 классов, 20 примеров на класс. Во втором 100 классов из Imagenet — по 600 картинок на класс. Ещё есть раздел про RL, но его я не смотрел.


      Перед обучением все классы разбиваются на непересекающиеся множества train, validation и test. Для проверки, из test-классов (которые модель не видела при обучении) выбирается, например, 5 случайных классов (5-way learning). Для каждого из выбранных классов сэмплится несколько примеров, лейблы кодируются one-hot вектором длины 5. Дальше примеры для каждого класса делятся на две части A и B. Примеры из A показываются модели с ответами, а примеры из B используются для проверки точности классификации. Так формируется задание. Авторы смотрят на accuracy.


      Таким образом, нужно научить модель адаптироваться к новому заданию (новому набору классов) за несколько итераций/новых примеров.


      В отличии от предыдущих работ, где для этого пытались использовать RNN или feature embedding с непараметрическими методами на тесте (вроде k-ближайших соседей), авторы предлагают подход, позволяющий настроить параметры любой стандартной модели, если она обучается градиентными методами.


      Ключевая идея: обновлять веса модели так, чтобы она давала лучший результат на новых заданиях.
      Интуиция: внутри модели получатся универсальные для всех классов датасета представления входных данных, по которым модель сможет быстро подстроиться под новое задание.


      Суть в следующем. Пусть мы хотим, чтобы наша модель F(x, p) обучалась новому заданию за 1 итерацию (1-shot learning). Тогда для обучения нужно из тренировочных классов подготовить такие же задания как на тесте. Дальше на примерах из части A мы считаем loss, его градиент и делаем одну итерацию обучения — в итоге получаем промежуточные обновленные веса p' = p - a*grad и новую версию модели — F(x, p'). Считаем loss для F(x, p') на B и минимизируем его относительно исходных весов p. Получаем настоящие новые веса — конец итерации. Когда считается градиент от градиента :xzibit:, появляются вторые производные.


      На самом деле, генерируется сразу несколько заданий, объединеных в metabatch. Для каждого находится свой p' и считается свой loss. Потом все эти loss суммируются в total_loss, который уже минимизируется относительно p.


      Авторы применили свой подход к базовым моделям из предыдущих работ (маленькие сверточная и полносвязная сети) и получили SOTA на обоих датасетах.


      При этом, итоговая модель получается без дополнительных параметров для мета-обучения. Зато используется большое количество вычислений, в том числе из-за вторых производных. Авторы пробовали отбросить вторые производные на miniImagenet. При этом accuracy осталась почти такой же, а вычисления ускорились на 33%. Предположительно это связано с тем, что ReLU — кусочно-линейная функция и вторая производная у неё почти всегда нулевая.


      Код авторов на Tensorflow: https://github.com/cbfinn/maml. Там внутренний градиентый шаг делается вручную, а внешний с помощью AdamOptimizer.


      За редактуру спасибо yuli_semenova.

      Original source: habrahabr.ru (comments, light).

      https://habrahabr.ru/post/336624/


      Ежемесячная рубрика «Читаем статьи за Вас». Август 2017

      Четверг, 14 Сентября 2017 г. 14:09 + в цитатник
      grisme сегодня в 14:09 Разработка

      Ежемесячная рубрика «Читаем статьи за Вас». Август 2017

        image


        Привет, Хабр!
        С этого выпуска мы начинаем хорошую традицию: каждый месяц будет выходить набор рецензий на некоторые научные статьи от членов сообщества Open Data Science из канала #article_essence. Хотите получать их раньше всех — вступайте в сообщество ODS!
        Статьи выбираются либо из личного интереса, либо из-за близости к проходящим сейчас соревнованиям. Если вы хотите предложить свою статью или у вас есть какие-то пожелания — просто напишите в комментариях и мы постараемся всё учесть в дальнейшем.


        Статьи на сегодня:


        1. Fitting to Noise or Nothing At All: Machine Learning in Markets
        2. Learned in Translation: Contextualized Word Vectors
        3. Create Anime Characters with A.I. !
        4. LiveMaps: Converting Map Images into Interactive Maps
        5. Random Erasing Data Augmentation
        6. YellowFin and the Art of Momentum Tuning
        7. The Devil is in the Decoder
        8. Improving Deep Learning using Generic Data Augmentation
        9. Learning both Weights and Connections for Efficient Neural Networks
        10. Focal Loss for Dense Object Detection
        11. Borrowing Treasures from the Wealthy: Deep Transfer Learning through Selective Joint Fine-tuning
        12. Model-Agnostic Meta-Learning for Fast Adaptation of Deep Networks

        1. Fitting to Noise or Nothing At All: Machine Learning in Markets


        Оригинал статьи
        Автор: kt{at}ut{dot}ee


        Это рецензия на статью, в которой диплернингом предсказываются финансовые рынки. Автор поста (далее — ZHD) указывает на следующие очевидные глупости данной и схожих статей:


        • Притягивание за уши показателя accuracy методом взятия максимума из всех экспериментов. Медиана, наоборот, как раз соответствует случайному предсказанию.
        • Хвастовство "хорошими результатами", которые получены на инструментах, которые сейчас вообще не торгуются (т.е. у них константная цена). В целом, ZHD рекомендует сравнивать трейдинговые модели не со случайным предсказателем, а со стратегией buy and hold.
        • Авторы "симулируют" свою стратегию, предполагая отсутствие комиссий при сделках и то, что любую сделку можно выполнить по цене, равной среднему между high и low за последние пять минут. Из-за этого в симуляциях наилучшая прибыль получается у наименее ликвидных инструментов (которые на деле далеко не всегда продадутся по средней цене за пять минут, поэтому вся прибыль скорее всего "получена" за счет нечестного предположения о ликвидности). Вдобавок, авторы хвастаются только несколькими "удачными" моделями, умалчивая всё, что отсимулировалось в минус.
        • Авторы не учитывают время работы бирж (в том числе, очные сессии для сделок (trading pits)) и по мнению ZHD это неправильно.

        2. Learned in Translation: Contextualized Word Vectors


        Оригинал статьи
        Код
        Автор: kt{at}ut{dot}ee


        Трансфер лернинг в наше время — стандартный прием. Взять сетку, натренированную на породах болонок из ImageNet, и прикрутить её куда-то для распознавания типов соплей — это уже стандартная программа для всех мамкиных диплёрнеров. В контексте обработки текста, трансфер лернинг обычно менее глубокий и упирается в использование заготовленных векторов слов типа Word2Vec, GloVe итд.


        Авторы статьи предлагают углубить текстовый трансфер лернинг на один уровень следующим образом:


        • Натренируем LSTM-based seq2seq модель (вида encoder + decoder with attention over encoder hidden states) для перевода, скажем, с английского на немецкий.
        • Возьмем из неё только encoder (он будет простенького вида LSTM(Embedding(word_idxs)). Этот энкодер умеет превращать последовательность слов в последовательность LSTM hidden states. Т.к. эти hidden states взяты не случайно (их переводческая модель использует в своём аттеншене), то однозначно в них будет полезный сигнал.
        • Ну и всё, давайте теперь строить любые другие текстовые модели, которым мы на вход будем скармливать не только GloVe векторы слов, но и приклеенные к ним соответствующие LSTM hidden векторы из нашего переводческого энкодера (их мы будем называть Context Vectors, CoVe).

        Далее авторы наворачивают какую-то нетривиальную модель c biattention и maxout (видимо, завалялась с их прошлой работы) и сравнивают, как она работает в разных задачах, если ей на входе скормить случайный эмбеддинг, GloVe, GloVe+CoVe, GloVe+CoVe+CharNGramEmbeddings.


        По результатам кажется, что добавление CoVe повышает точность голого GloVe примерно на 1%. Иногда эффект меньше, иногда отрицательный, иногда добавление вместо CoVe в модель CharNGrams работает так же или даже лучше. В любом случае, совмещение GloVe+CoVe+CharNGrams работает точно лучше всех других методов.


        image


        На мой взгляд, из-за того, что авторы навернули нехилую модель с аттеншеном поверх сравниваемых типов эмбеддинга (GloVe vs CoVe), замер эффекта полезности CoVe получился излишне зашумленным и не очень убедительным. Я бы предпочел увидеть более "лабораторный" замер.


        3. Create Anime Characters with A.I. !


        Оригинал статьи
        Сайт
        Автор: kt{at}ut{dot}ee


        Существует сайт Getchu, на котором собраны "профили" анимешных героев различных японских игр с картинками-иллюстрациями. Эти картинки можно скачать.


        Для нахождения лица на картинке можно использовать некую тулзу "lbpcascade animeface". Таким образом авторы получили 42к анимешных лиц, которые они потом ручками пересмотрели и выкинули 4% плохих примеров.


        Имеется некая уже готовая CNN-модель Illustration2Vec, которая умеет распознавать на анимешных картинках свойства типа "улыбка", "цвет волос", итд. Авторы использовали её для того, чтобы отлейблить картинки и выбрали 34 интересующих их тэга.
        Авторы запихнули это всё в DRAGAN (Kodali et al, где это отличается от обычных GAN авторы не углубляются, видимо, непринципиально).
        Чтобы уметь генерировать картинки с заданными атрибутами, авторы делают как в ACGAN:


        • Кормят генератору вектор атрибутов.
        • Заставляют дискриминатор этот вектор предсказывать.
        • Дополнительно штрафуют генератор пропорционально тому, насколько дискриминатор не угадал верный класс.

        И генератор, и дискриминатор — довольно замороченные конволюционные SRResNet-ы (у генератора 16-блоковый, у дискриминатора 10-блоковый). У дискриминатора авторы убрали батчнорм-слои "since it would bring correlations within the mini-batch, which is undesired for the computation of the gradient norm." Я не очень понял эту проблему, поясните если вдруг кому ясно.
        Тренировалось всё Адамом с уменьшающимся lr начиная от 0.0002, не очень понятно как долго.
        Для вебаппа авторы сконвертировали сеть под WebDNN (https://github.com/mil-tokyo/webdnn) и поэтому генерят все картинки прямо в браузере на клиенте (!).


        4. LiveMaps: Converting Map Images into Interactive Maps


        [Оригинал статьи](http://dl.acm.org/citation.cfm?id=3080673, https://doi.org/10.1145/3077136.3080673)
        Статья — победитель Best Short Paper Awart SIGIR 2017
        Автор: zevsone


        Предложена принципиально новая система (LiveMaps) для анализа изображений карт и извлечения релевантного viewport'a.


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


        LiveMaps работает в несколько приемов. Сперва проверяется является ли изображение картой.
        Если "да", система пытается определить геолокацию этого изображения. Для определения локации используется текстовая и визуальная информация извлеченная из изображения. В итоге, система создает интерактивную карту, отображающую географическую область, вычисленную для изображения.


        Результаты оценки на датасете локаций с высоким рангом показывают, что система способна конструировать очень точные интерактивные карты, достигая при этом хорошего покрытия.


        P.S. Совсем не ожидал получить best short paper award на такой маститой конфе (121 short paper в претендентах в этом году, все industry гиганты).


        5. Random Erasing Data Augmentation


        Оригинал статьи
        Автор: egor.v.panfilov{at}gmail{dot}com


        Статья посвящена исследованию одного из простейших методов аугментации изображений — Random Erasing (по-русски, закрашиванию случайных прямоугольников).


        Аугментация параметризовалась 4-мя параметрами: (P_prob) вероятность применения к каждой картинке батча, (P_area) размер региона (area ratio), (P_aspect) соотношение сторон региона (aspect ratio), (P_value) заполняемое значением: случайные / среднее на ImageNet / 0 / 255.


        image


        Авторы провели оценку влияния данного метода аугментации на 3-х задачах: (A) object classification, (B) object detection, © person re-identification.


        (A): Использовались 6 архитектур, начиная с AlexNet, заканчивая ResNeXt. Датасет — CIFAR10/100. Оптимальное значение параметров: P_prob = 0.5, P_aspect = в широком диапазоне, но желательно не 1(квадрат), P_area = 0.02-0.4 (2-40% изображения), P_value = random или среднее на ImageNet, для 0 и 255 результаты существенно хуже. Сравнили также с другими методами и аугментации (random cropping, random flipping), и регуляризации (dropout, random noise): в порядке снижения эффективности — всё вместе, random cropping, random flipping, random erasing. Drouput и random noise данный метод уделывет "в щепки". В целом, метод не самый мощный, но при оптимальных параметрах стабильно даёт 1% точности (5.5% -> 4.5%). Также пишут, что он повышает робастность классификатора к перекрытиям объектов :you-dont-say:.


        (B): Использовалась Fast-RCNN на PASCAL VOC 2007+2012. Реализовали 3 схемы: IRE (Image-aware Random Erasing, также выбираем регион для зануления вслепую), ORE (Object-aware, зануляем только части bounding box'ов), I+ORE (и там, и там). Существенной разницы по mAP между этими методами нет. По сравнению с чистым Fast-RCNN, дают порядка 5% (67->71 на VOC07, 70->75 на VOC07+12), столько же, сколько и A-Fast-RCNN. Оптимальные параметры — P_prob = 0.5, P_area = 0.02-0.2 (2-20%), P_aspect = 0.3-3.33 (от лежачей до стоячей полоски).


        ©: Использовались ID-discim.Embedding (IDE), Triplet Net, и SVD-Net (все на базе ResNet, предобученной на ImageNet) на Market-1501 / DukeMTMC-reID / CUHK03. На всех моделях и датасетах аугментация стабильно даёт не менее 2% (до 8%) для Rank@1, и не менее 3% (до 8%) для mAP. Параметры те же, что и для (B).


        В целом, несмотря на простоту метода, исследование и статья довольно аккуратные и подробные (10 страниц), с кучей графиков и таблиц. Порадовали китайцы, ничего не скажешь.


        6. YellowFin and the Art of Momentum Tuning


        Оригинал статьи
        Дополнительный материал про momentum
        Автор: Arech


        Давно я её читал, поэтому очень-очень поверхностно: после вдумчивого курения свойств классического моментума (он же моментум Бориса Поляка), на одномерных строго выпуклых квадратичных целях чуваки вывели (или нашли у кого-то?) неравенство, связывающее коэффициенты learning rate и моментума так, чтобы они попадали в некий "робастный" регион, гарантирующий наибыстрейшее схождение алгоритма SGD. Потом показали(?), что оное утверждение в принципе может как-то выполняться и для некоторых невыпуклых функций, по крайней мере в некоторой их локальной области, которую можно более-менее апроксимировать квадратичным приближением. После чего решили запилить свой тюнер YellowFin, который на основании знания предыдущей истории изменения градиента, апроксимировал бы нужные для неравенства характеристики поверхности функции ошибки (дисперсия градиента, некая "обощённая" кривизна поверхности и расстояние до локального минимума квадратичной апроксимации текущей точки) и, с помощью этих апроксимаций, выдавал бы подходящие значения learning rate и моментума для использования в SGD.


        Также, изучив вопрос асинхронной (распределённой) тренировки сетей, чуваки предложили обобщение своего метода (Closed-loop YellowFin), которое учитывает, что реальный моментум в таких условиях оказывается больше запланированного.


        Тестировали свёрточные 110- и 164-слойные ResNet на CIFAR10 и 100 соответственно, и какие-то LSTM на PTB, TS и WSJ. Результаты интересные (от х1.18 до х2.8 ускорения относительно Адама), но, как обычно, есть вопросы к постановке эксперимента — грубый подбор коэффициентов для компетиторов, + емнип, только один прогон на каждую архитектуру, + показаны результаты только тренировочного набора… Короче, есть к чему докопаться...


        Как-то так, надеюсь, по деталям не сильно наврал.


        Я подумывал запилить его к своей либине, но крепко влип в Self-Normalizing Neural Networks (SELU+AlphaDropout), которыми занялся чуть раньше и которые оказались мне крайне полезны, поэтому пока руки не дошли. Слежу за тем тредом в Lesagne (https://github.com/Lasagne/Lasagne/issues/856 — у человека возникли проблемы с воспроизводством результатов) и вообще, надеюсь, что будет больше инфы по воспроизводству их результатов. Так что если кто попробует — делитесь чокак.


        7. The Devil is in the Decoder


        Оригинал статьи
        Автор: ternaus


        Вопрос с тем, какой UpSampling лучше для различных архитектур, где есть Decoder, теребит умы многих, особенно тех, кто борется за пятый знак после запятой в задачах сегментации, super resolution, colorization, depth, boundary detection.


        Ребята из Гугла и UCL заморочились и написали статью, где эмпирическим путем решили проверить кто лучше и найти в этом логику.


        Проверили — выяснилось, что разница есть, но логика не очень просматривается.


        Для сегментации:


        [1] Transposed Conv = Upsampling + Conv и который все яростно используют в Unet норм.
        [2] добрасывание skiped connections, то есть, трансформация SegNet => Unet железобетонно добрасывает. Это интуитивно понятно, но тут есть циферки.
        [3] Такое ощущение, что Separable Transposed, которое Transposed, но с меньшим числом параметоров для сегментации, работает лучше. Хотелось бы, чтобы народ в #proj_cars это проверил.
        [4] Хитроумный Bilinear additive Upsampling, который они предложили, на сегментации работает примерно как [3]. Но это тоже в сторону коллектива из #proj_cars проверить
        [5] Они добрасывают residual connections куда-то, что тоже в теории может что-то добавить, но куда именно — не очень понятно, и добрасывает очень неуверенно и не всегда.


        Для задач сегментации они используют resnet 50 как base и потом сверху добавляют decoder.


        Для задачи instance boundary detection они решили выбрать метрику, при которой меньше оверфитишь на алгоритм разметки + циферки побольше получаются.
        То есть, During the evaluation, predicted contour pixels within three from ground truth pixels are assumed to be correct. Что сразу ставит вопрос о том, как это все переносится на задачи, где важен каждый пиксель. (Тут вспоминается Костин трюк со спутников для поиска заборов толщиной в один пиксель и то, как народ борется за +-1 пиксель на границах в задаче про машинки)


        [6] У всех сетей, что они тренируют, у весов используется L2 регуляризация порядка 0.0002
        Карпатый, вроде, тоже в свое время говорил, что всегда так делает для более стабильной сходимости. (Это мне надо попробовать, если кто так делает и это дает что-то заметное, было бы неплохо рассказать об этом в тредике)


        Summary:
        [1] Вопрос о том, кто и когда лучше они подняли, но не ответили.
        [2] Они предложили еще один метод делать Upsampling, который работает не хуже других.
        [3] Подтвердили, что skipped connection однозначно помогают, а residual — в зависимости от фазы луны.


        Ждем месяц, о том, что скажет человеческий GridSearch на #proj_cars.


        8. Improving Deep Learning using Generic Data Augmentation


        Оригинал статьи
        Автор: egor.v.panfilov{at}gmail{dot}com


        Эпиграф: Лавры китайцев не дают покоя никому, даже на чёрном континенте. Хороших компьютеров, правда, пока не завезли, но Тернаус велел писать, "и вот они продолжают".


        Авторы попытались провести бенчмарк методов аугментации изображений на задаче классификации изображений, и выработать рекомендации по их использованию для различных случаев. В первом приближении, данные методы разделить на 2 категории: Generic (общеприменимые) и Complex (использующие информацию о домене / генеративные). В данной статье рассматриваются только Generic.


        Все эксперименты в статье проводились на Caltech-101 (101 класс, 9144 изображения) с использованием ZFNet (половина информативной части статьи о том, как лучше всего обучить ванильную ZFNet). Обучали 30 эпох, используя DL4j. Рассматриваемые методы аугментации: (1) без аугмен-ции, (2-4) геометрические: horizontal flipping, rotating (-30deg и +30deg), cropping (4 угловых кропа), (5-7) фотометрические: color jittering, edge enhacement (добавляем к изображению результат фильтра Собеля), fancy PCA (усиливаем principal компоненты на изображениях).


        Результаты: к бейзлайну (top1/top5: 48.1%/64.5%) (a) flipping даёт +1/2%, но увеличивает разброс точности, (b) rotating даёт +2%, © cropping даёт +14%, (d) color jittering +1.5/2.5%, (d-e) edge enhancement и fancy PCA по +1/2%. Т.е. среди геометрических методов впереди cropping, среди фотометрических — color jittering. В заключении авторы пишут, что существенное улучшение точности при аугментации cropping'ом может быть связано с тем, что датасет получается в 4 раза больше исходного (сбалансировать-то не судьба). Из положительного — не забыли 5-fold кросс-валидацию при оценке точности моделей. Почему были выбраны конкретно эти методы аугментации среди других (в т.ч. более популярных) узнаем, видимо, в следующей статье.


        9. Learning both Weights and Connections for Efficient Neural Networks


        Оригинал статьи
        Автор: egor.v.panfilov{at}gmail{dot}com


        Статья рассматривает проблему выского потребления ресурсов современными архитектурами DNN (в частности, CNN). Самый главный бич — это обращение к динамической памяти. Например, инференс сети с 1 млрд связей на 20Гц потребляет ~13Вт.


        Авторы предлагают метод снижения числа активных нейронов и связей в сети — prunning. Работает следующим образом: (1) обучаем сеть на полном датасете, (2) маскируем связи с весами ниже определённого уровня, (3) дообучаем оставшиеся связи на полном датасете. Шаги (2) и (3) можно и нужно повторять несколько раз, так как агрессивный (за 1 подход) prunning показывает результаты немного хуже (для AlexNet на ImageNet, например, 5x против 9x). Хитрости: использовать L2-регуляризацию весов, при дообучении уменьшать dropout, уменьшать learning rate, прунить и дообучать CONV и FC слои раздельно, выкидывать мёртвые (non-connected) нейроны по результатам шага (2).


        Эксперименты производились в Caffe с сетями Lenet-300-100, Lenet-5 на MNIST, AlexNet, VGG-16 на ImageNet. На MNIST: уменьшили число весов и FLOP'ов в 12 раз, а также обнаружили, что prunning проявляет свойство механизма attention (по краям режет больше). На ImageNet: AlexNet обучали 75 часов и дообучали 173 часа, VGG-16 прунили и дообучали 5 раз. По весам удалось сжать в 9 и 13 раз соответственно, по FLOP'ам — в 3.3 и 5 раз. Интересен профиль того, как прунятся связи: первый CONV-слой ужимается меньше чем в 2 раза, следующие CONV — в 3 и более (до 12), скрытые FC — в 10-20 раз, последний FC слой — в 4 раза.


        В заключение, авторы приводят результаты сравнения различных методов prunning'а (с L1-, L2-регуляризацией, с дообучением и без, отдельно по CONV, отдельно по FC). Вкратце, есть лень прунить, то учите сеть с L1, и половину слоёв можно просто выкинуть. Если не лень — только L2, прунить и дообучать итеративно ~5 раз. И последнее, хранение весов в sparse виде у авторов давало overhead в ~16% — не то чтобы критично, когда у вас сеть в 10 раз меньше.
        image


        10. Focal Loss for Dense Object Detection


        Оригинал статьи
        Автор: kt{at}ut{dot}ee


        Как известно, процесс поиска моделей в машинном обучении упирается в оптимизацию некой целевой функции потерь. Самая простая функция потерь — процент ошибок на тренинг-сете, но её оптимизировать сложно и результат статистически плох, поэтому на практике используют разные суррогатные лоссы: квадрат ошибки, минус логарифм вероятности, экспонента от минус скора, hinge loss и т.д. Все суррогатные лоссы являются монотонными функциями, штрафующими ошибки тем больше, чем больше величина ошибки. Любой лосс можно интерпретировать как вид распределения целевой переменной (например, квадрат ошибок соответствует гауссовому распределению).


        Авторы работы обнаружили, что суррогатный лосс вида


        loss(p, y) := -(1-p)^gamma log(p) if y == 1 else -p^gamma log(1-p)


        ещё нигде не использовался и не публиковался. Зачем нужно использовать именно такой лосс, а не ещё какой-нибудь, и каков смысл подразумеваемого распределения, авторы не знают, но им он кажется прикольным, т.к. здесь есть лишний параметр gamma, с помощью которого можно как будто бы подкручивать величину штрафа "легким" примерам. Авторы назвали эту функцию "focal loss".


        Авторы подобрали один набор данных и одну модель-нейросеть на которых, если подогнать значения параметра, в табличке результатов виден якобы некий положительный эффект от использования такого лосса вместо обычной кросс-энтропии (взвешенной по классам). На самом деле, большая часть статьи жует вопросы применения RetinaNet для детекции объектов, не сильно зависящие от выбора лосс-функции.


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


        Альтернативное мнение
        Следи за руками: чуваки взяли один из стандартных, довольно сложных датасетов, на которых меряются в детекции, взяли простенькую сеть без особых наворотов (то из чего все другие уже пробовали выжать сколько могли), приложили свой лосс и получили сходу результат single model без мультскейла и прочих трюков выше чем все остальные на этом датасете, включая все сети в одну стадию и более продвинутые двухстадийные. Чтобы убедиться, что дело в лоссе, а не чём-то ещё, они попробовали другие варианты, которые до этого были крутыми и модными техниками — балансирование кросс-энтропии, OHEM и получили результат стабильно выше на своём. Покрутили параметры своего и нашли вариант, который работает лучше всех и даже попробовали чуток объяснить почему (там есть график, где видно что гамма ниже 2 даёт довольно гладкое распределение, а больше двух уж очень резко штрафует (даже при двух там практически полка, удивительно что работает)).
        Конечно, можно было им начать сравнивать 40 вариантов сетей, с миллионом вариантов гиперпараметров и на всех известных датасетах, да ещё и кроссвалидацию 10 раз по 10 фолдов, но сколько бы это времени тогда заняло и когда была бы публикация готова и сколько бы там было разных идей?
        Тут всё просто — изменили один компонент, получили результат превосходящий SoTA на конкретном датасете. Убедились, что результат вызван изменением, а не чем-то ещё. Fin.


        Альтернативное мнение
        Наверное, стоит добавить, что если бы статья позиционировалась как представление RetinaNet, она бы смотрелась, по-моему, совсем по-другому. Она ведь и построена на самом-то деле в основном как пример применения RetinaNet. Зачем в неё внезапно вставили упор на лосс и странный заголовок, мне лично непонятно. Объективных замеров, подтверждающих высказанные про этот лосс тезисы там всё-таки нет.


        Может быть, например, RetinaNet планируется опубликовать в более серьезном формате и с другим порядком авторов, а это — просто продукт стороннего эксперимента, который тоже решили лишней публикацией оформить потому что студент ведь работал и молодец. В таком случае это опять же пример того, как высосать лишнюю статью из воздуха.


        В любом случае, я для себя никак не могу вынести из этой статьи обещанный в заглавии и тексте тезис "вкручивайте такой лосс везде и будет вам счастье".


        Тезис "RetinaNet хорошо работает на COCO (с любым лоссом, причем!)" я, при этом, вынести вполне могу.


        11. Borrowing Treasures from the Wealthy: Deep Transfer Learning through Selective Joint Fine-tuning


        Оригинал статьи
        Код
        Автор: movchan74


        image


        Авторы сосредоточились на проблеме классификации изображений с небольшим датасетом. Типичный подход в таком случае следующий: взять предобученную CNN на ImageNet и дообучить на своем датасете (или даже дообучить только классификационные полносвязный слои). Но при этом сеть быстро переобучается и достигает не таких значений точности, каких хотелось бы. Авторы предлагают использовать не только целевой датасет (target dataset, мало данных, далее буду называть датасет T), но и дополнительный исходный датесет (source dataset, далее буду называть датасет S) с большим количеством изображений (обычно ImageNet) и обучать мультитаск на двух датасетах одновременно (делаем после CNN две головы по одной на датасет).


        Но как выяснили авторы, использование всего датасета S для обучения не очень хорошая идея, лучше использовать некоторое подмножество датасета S. И далее в статье предлагаются различные способы поиска оптимального подмножества.


        Получаем следующий фреймворк:


        1. Берем два датасета: S и T. T — датасет с малым количеством примеров, для которого нам необходимо получить классификатор, S — большой вспомогательный датасет (обычно ImageNet).
        2. Выбираем подмножество изображений из датасета S, такое, что изображения из подмножества близки к изображениям из целевого датасета T. Как именно выбирать близкие рассмотрим далее.
        3. Учим мультитаск сеть на датасете T и выбранном подмножесте S.

        Рассмотрим как предлагается выбирать подмножество датасета S. Авторы предлагают для каждого семпла из датасета T находить некоторое количество соседей из S и учиться только на них. Близость определяется как расстояние между гистограммами низкоуровневых фильтров AlexNet или фильтров Габора. Гистограммы берутся, чтобы не учитывать пространственную составляющую.


        Объяснение, почему берутся низкоуровневые фильтры, приводится следующее:


        1. Получается лучше обучить низкоуровневые сверточные слои за счет большего количества данных, а качество этих низкоуровневых фич определяет качество фич более высоких уровней.
        2. Поиск похожих изображений по низкоуровневым фильтрам позволяет находить намного больше семплов для тренировки, т.к. семантика почти не учитывается.
          Мне, если честно, эти объяснения не очень нравятся, но в статье так. Может я конечно что-то не понял или понял не так. Это все описано на 2 странице после слов "The motivation behind selecting images according to their low-level characteristics is two fold".

        Еще из особенностей поиска близких изображений:


        1. Гистограммы строятся так, чтобы в среднем по всему датасету в один бин попадало примерно одинаковое количество.
        2. Расстояние между гистограммами считается с помошью KL-дивергенции.

        Авторы попробовали разные сверточные слои AlexNet и фильтры Габора для поиска близких семплов, лучше всего получилось если использовать 1+2 сверточные слои из AlexNet.


        Также авторы предложили итеративный способ подбора количества похожих семплов из датасета S для каджого семпла из T. Изначально берем некоторое заданое число ближайших соседей для каждого отдельного семпла из T. Затем прогоняем обучение, и если ошибка для семпла большая, то увеличиваем количество ближайших соседей для этого семпла. Как именно производится увеличение ближайших соседей понятно из уравнения 6.


        Из особенностей обучения. При формировании батча случайно выбираем семплы из датасета T, и для каждого выбранного семпла берем одного из его ближайших соседей.


        Проведены эксперименты на следующих датасетах: Stanford Dogs 120, Oxford Flowers 102, Caltech 256, MIT Indoor 67. На всех датасетах получены SOTA результаты. Получилось поднять точность классфикации от 2 до 10% в зависимости от датасета.


        12. Model-Agnostic Meta-Learning for Fast Adaptation of Deep Networks


        Оригинал статьи
        Код
        Автор: repyevsky{at}gmail{dot}com
        Статья про мета-обучение: авторы хотят научить модель объединять свой предыдущий опыт с малым количеством новой информации для решения новых заданий из некоторого общего класса.


        Чтобы было понятно чего хотят добиться авторы, расскажу как оценивается модель.


        В качество бенчмарка для классификации используются два датасета: Omniglot и miniImagenet. В первом рукописные буквы из нескольких алфавитов — всего около 1600 классов, 20 примеров на класс. Во втором 100 классов из Imagenet — по 600 картинок на класс. Ещё есть раздел про RL, но его я не смотрел.


        Перед обучением все классы разбиваются на непересекающиеся множества train, validation и test. Для проверки, из test-классов (которые модель не видела при обучении) выбирается, например, 5 случайных классов (5-way learning). Для каждого из выбранных классов сэмплится несколько примеров, лейблы кодируются one-hot вектором длины 5. Дальше примеры для каждого класса делятся на две части A и B. Примеры из A показываются модели с ответами, а примеры из B используются для проверки точности классификации. Так формируется задание. Авторы смотрят на accuracy.


        Таким образом, нужно научить модель адаптироваться к новому заданию (новому набору классов) за несколько итераций/новых примеров.


        В отличии от предыдущих работ, где для этого пытались использовать RNN или feature embedding с непараметрическими методами на тесте (вроде k-ближайших соседей), авторы предлагают подход, позволяющий настроить параметры любой стандартной модели, если она обучается градиентными методами.


        Ключевая идея: обновлять веса модели так, чтобы она давала лучший результат на новых заданиях.
        Интуиция: внутри модели получатся универсальные для всех классов датасета представления входных данных, по которым модель сможет быстро подстроиться под новое задание.


        Суть в следующем. Пусть мы хотим, чтобы наша модель F(x, p) обучалась новому заданию за 1 итерацию (1-shot learning). Тогда для обучения нужно из тренировочных классов подготовить такие же задания как на тесте. Дальше на примерах из части A мы считаем loss, его градиент и делаем одну итерацию обучения — в итоге получаем промежуточные обновленные веса p' = p - a*grad и новую версию модели — F(x, p'). Считаем loss для F(x, p') на B и минимизируем его относительно исходных весов p. Получаем настоящие новые веса — конец итерации. Когда считается градиент от градиента :xzibit:, появляются вторые производные.


        На самом деле, генерируется сразу несколько заданий, объединеных в metabatch. Для каждого находится свой p' и считается свой loss. Потом все эти loss суммируются в total_loss, который уже минимизируется относительно p.


        Авторы применили свой подход к базовым моделям из предыдущих работ (маленькие сверточная и полносвязная сети) и получили SOTA на обоих датасетах.


        При этом, итоговая модель получается без дополнительных параметров для мета-обучения. Зато используется большое количество вычислений, в том числе из-за вторых производных. Авторы пробовали отбросить вторые производные на miniImagenet. При этом accuracy осталась почти такой же, а вычисления ускорились на 33%. Предположительно это связано с тем, что ReLU — кусочно-линейная функция и вторая производная у неё почти всегда нулевая.


        Код авторов на Tensorflow: https://github.com/cbfinn/maml. Там внутренний градиентый шаг делается вручную, а внешний с помощью AdamOptimizer.


        За редактуру спасибо yuli_semenova.

        Original source: habrahabr.ru (comments, light).

        https://habrahabr.ru/post/336624/


        Обзор инструментария для нагрузочного и перформанс-тестирования

        Четверг, 14 Сентября 2017 г. 13:56 + в цитатник
        ValeriaKhokha сегодня в 13:56 Разработка

        Обзор инструментария для нагрузочного и перформанс-тестирования

          Как говорят иные отважные люди: «От dev до prod — всего один шаг». Люди опытные добавляют, что шаг этот называется «тестирование», причём самое разнообразное, и нам просто нет смысла им не верить.



          Нагрузка имеет значение: водитель этого грузовика умудрился обрушить мост весом своего ТС, счёт за восстановление составил примерно 16m. К счастью, тестирование ПО обходится дешевле!

          Конечно, говоря о тестировании, нужно понять, с чем и за что мы боремся. Мы сознательно ограничили себя и решили сегодня поговорить исключительно про нагрузочное тестирование и тестирование производительности: темы, полярно удалённые друг от друга, крайне интересны в самом практическом выражении. Рассмотрим инструменты для того и другого, не привязываясь к какому-то конкретному стеку технологий, так что не удивляйтесь соседству Яндекс.Танк и BenchmarkDotNet!

          Нагрузочное тестирование


          Представим, что мы с вами написали некий сервис — теперь нужно понять, какую нагрузку он выдержит. Старая грустная шутка разработки качественного ПО гласит, что лучше иметь софт, который работает гарантированно плохо, чем софт, работающий хоть и хорошо, но негарантированно хорошо: в первом случае мы хотя бы будем знать, на что твёрдо можем рассчитывать. Далее, если наш сервис умеет масштабироваться тем или иным образом, то нужно понять, насколько с ростом нагрузки масштабирование оказывается полезным и выполняет ли оно возложенные на него проектом задачи.

          Что ж, берём и направляем на наше детище нагрузку, притом внимательно наблюдая за результатом: нас, очевидно, интересует ситуация, когда сервис либо станет отвечать на запросы с неприемлемой задержкой, либо будет возвращать неверные данные, либо вовсе перестанет подавать признаки жизни для всех запросов или лишь для их части.

          Давайте представим, что мы с вами написали некоторый сервис — для определённости скажем, что веб-сервис, но это не столь важно. Чтобы убедиться, на что мы с ним можем рассчитывать, мы начинаем «обстреливать» его запросами, наблюдая за поведением как самого сервиса, так и за нагрузкой на серверах, где он крутится. Хорошо, если заранее понятно, какие запросы нам нужно отправлять сервису (в этом случае мы можем подготовить массив запросов заранее, а после отправить его в наше приложение одним махом). Если же второй запрос зависит от результатов первого (простой пример — сначала происходит авторизация пользователя, и в следующие обращения к сервису включается информация об ID сессии), то генератор нагрузки должен быть способен генерировать тестовые запросы крайне быстро, в реальном времени.

          С учётом обстоятельств и нашего знания об объекте тестирования выбираем инструмент(ы):

          JMeter


          Да, старый добрый JMeter. Он вот уже без малого двадцать лет (!) является частым выбором для многих вариантов и типов нагрузочного тестирования: удобный GUI, независимость от платформы (спасибо Java!), поддержка многопоточности, расширяемость, отличные возможности по созданию отчётов, поддержка многих протоколов для запросов. Благодаря модульной архитектуре JMeter можно расширить в нужную пользователю сторону, реализуя даже весьма экзотические сценарии тестирования — причем, если ни один из написанных сообществом за прошедшее время плагинов нас не устроит, можно взять API и написать собственный. При необходимости с JMeter можно выстроить, хоть и ограниченное, но распределённое тестирование, когда нагрузка будет создаваться сразу несколькими машинами.

          Одной из удобных функций JMeter является работа в режиме прокси: указываем в настройках браузера в качестве прокси «127.0.0.1:8080» и посещаем браузером нужные нам страницы нужного сайта, тем временем JMeter сохраняет все наши действия и все сопутствующие запросы в виде скрипта, который позже можно будет отредактировать, как нужно — это делает процесс создания HTTP-тестов заметно проще.

          Кстати, последняя версия (3.2), вышедшая в апреле этого года, научилась отдавать результаты тестирования в InfluxDB при помощи асинхронных HTTP-запросов. Правда, начиная как раз с версии 3.2, JMeter стал требовать только Java 8, но это, наверное, не самая высокая цена за прогресс.

          Хранение тестовых сценариев у JMeter реализовано в XML-файлах, что, как оказалось, создаёт массу проблем: их совсем неудобно писать руками (читай — для создания текста необходим GUI), как неудобна и работа с такими файлами в системах управления версиями (особенно в момент, когда нужно сделать diff). Конкурирующие на поле нагрузочного тестирования продукты, такие, как Яндекс.Танк или Taurus, научились самостоятельно и на лету формировать файлы с тестами и передавать их в JMeter на исполнение, таким образом пользуясь мощью и опытом JMeter, но давая возможность пользователям создавать тесты в виде более читаемых и легче хранимых в CVS тестовых скриптов.

          LoadRunner


          Ещё один давно существующий на рынке и в определенных кругах очень известный продукт, большему распространению которого помешала принятая компанией-производителем политика лицензирования (кстати, сегодня, после слияния подразделения ПО компании Hewlett Packard Enterprise с Micro Focus International, привычное название HPE LoadRunner сменилось на Micro Focus LoadRunner). Интерес представляет логика создания теста, где несколько (наверное, правильно сказать — «много») виртуальных пользователей параллельно что-то делают с тестируемым приложением. Это даёт возможность не только оценить способность приложения обработать поток одновременных запросов, но и понять, как влияет работа одних пользователей, активно что-то делающих с сервисом, на работу других. При этом речь идёт о широком выборе протоколов взаимодействия с тестируемым приложением.

          HP в своё время создала очень хороший набор инструментов автоматизации функционального и нагрузочного тестирования, которые, при необходимости, интегрируются в процесс разработки ПО, и LoadRunner умеет интегрироваться с ними (в частности, с HP Quality Center, HP QuickTest Professional).

          Некоторое время назад производитель решил повернуться лицом к тем, кто не готов сразу платить за лицензию, и поставляет LoadRunner с бесплатной лицензией (где вписан лимит на 50 виртуальных пользователей, и запрещена небольшая часть всего набора поддерживаемых протоколов), а деньги берутся за дальнейшее расширение возможностей. Сложно сказать, насколько это будет способствовать повышению интереса к этому, без сомнения, занимательному инструменту, при наличии у него столь сильных конкурентов.

          Gatling


          Весьма мощный и серьёзный инструмент (не зря названный в честь скорострельного пулемета) — в первую очередь, по причине производительности и широты поддержки протоколов «из коробки». Например, там, где нагрузочное тестирование с JMeter будет медленным и мучительным (увы, плагин поддержки работы с веб-сокетами не особо быстр, что идейно конфликтует со скоростью работы самих веб-сокетов), Galting почти наверняка создаст нужную нагрузку без особых сложностей.

          Следует учесть, что, в отличие от JMeter, Gatling не использует GUI и вообще считается средством, ориентированным на опытную, «грамотную» аудиторию, способную создать тестовый скрипт в виде текстового файла.

          Есть у Gatling и минусы, за которые его критикуют. Во-первых, документация могла бы быть и получше, во-вторых, для работы с ним неплохо знать Scala: и сам Gatling, как инструмент тестирования, и тестовые сценарии пишутся именно на этом языке. В-третьих, разработчики «иногда» в прошлом кардинально меняли API, в результате можно было обнаружить, что тесты, написанные полугодом ранее, «не идут» на новой версии, либо требуют доработки/миграции. У Gatling также отсутствует возможность делать распределённое тестирование, что ограничивает возможные области применения.

          Яндекс.Танк


          Если коротко, Yandex Tank — это враппер над несколькими утилитами нагрузочного тестирования (включая JMeter), предоставляющий унифицированный интерфейс для их конфигурации, запуска и построения отчётов вне зависимости от того, какая утилита используется «под капотом».

          Он умеет следить за основными метриками тестируемого приложения (процессор, память, своп и пр.), за ресурсами системы (свободная память/место на диске), может остановить тест на основе разных понятных критериев («если время отклика превышает заданное значение», «если количество ошибок за единицу времени выше, чем х» и т.д). Кстати, умеет отображать в реальном времени основные статистические данные теста, что бывает очень полезно прямо в процессе теста.

          Танк используется и в самом Яндексе, и в других компаниях уже около 10 лет. Им обстреливают совершенно разные сервисы, с разными требованиями к сложности тестовых сценариев и к уровню нагрузки. Почти всегда для тестирования даже высоконагруженных сервисов хватает всего одного генератора нагрузки. Танк поддерживает разные генераторы нагрузки, как написанные специально для него (Phantom, BFG, Pandora), так и широко сторонние (JMeter). Модульная архитектура позволяет написать свой плагин под нужный генератор нагрузки и вообще прикрутить практически что угодно.

          Для чего использовать разные генераторы нагрузки? Phantom — это быстрая «пушка» на C++. Один такой генератор может выдать до сотни тысяч запросов в секунду. Но для достижения такой скорости приходится генерировать запросы заранее и нельзя (не получается) использовать получаемые от тестируемого сервиса данные для генерации очередного запроса. В случаях, когда нужно исполнять сложный сценарий или сервис использует нестандартный протокол, следует использовать JMeter, BFG, Pandora.

          В BFG, в отличие от Jmeter, нет GUI, тестовые сценарии пишутся на Python. Это позволяет использовать любые библиотеки (а их огромное количество). Часто бывает, что для сервиса написаны биндинги для Python, тогда их удобно использовать при написании нагрузочных сценариев. Pandora — это экспериментальная пушка на GoLang, достаточно быстрая и расширяемая, подходит для тестов по протоколу HTTP/2 и будет использоваться там, где нужны быстрые сценарии.

          Внутри Яндекса для хранения и отображения результатов нагрузочных тестов используется специальный сервис. Сейчас наружу открыт его упрощенный аналог под названием Overload — он полностью бесплатен, его используют, в том числе, для тестирования открытых библиотек (например) и проведения соревнований.

          Taurus


          Taurus — ещё один фреймворк над несколькими утилитами нагрузочного тестирования. Возможно, вам понравится этот продукт, использующий похожий на Яндекс.Танк подход, но имеющий несколько другой набор «фич», и, пожалуй, более адекватный формат конфигурационных файлов.

          Вообще, Taurus хорошо подойдёт в ситуации, когда мощность, скажем, Gatling важна для создания теста, но разбираться с Gatling (а также с написанием скриптов тестирования на Scala) нет желания или возможности: достаточно описать тест в куда более простом формате файла Taurus, настроить его на использование Gatling как инструмента создания нагрузки, и все Scala-файлы будут сгенерированы автоматически. Так сказать, «автоматизация автоматизации» в действии!

          Taurus можно настроить на отправку статистики теста на онлайн-сервис BlazeMeter.com, который отобразит данные в виде нарядных графиков и таблиц. Подход не очень обычный, но заслуживающий внимания: движок вывода отчётов, очевидно, совершенствуется со временем, и постепенно будет выводить информацию ещё более симпатично.

          Тестирование производительности


          Тестировать производительность сервиса или приложения можно и нужно не только после завершения процесса разработки, но и во время неё, буквально так же, как мы делаем регулярные юнит- или регрессионные тесты. Правильно организованные, регулярные тесты производительности позволяют ответить на очень «тонкий» вопрос: не привели ли последние изменения в коде приложения к ухудшению производительности получившегося ПО?

          Казалось бы, померить производительность — это так просто! Два раза взять timestamp (желательно с высокой точностью), посчитать разность, сложили-поделили, и всё — можно оптимизировать. Как бы не так! Хотя на словах этот вопрос звучит просто, на деле такого рода замеры довольно затруднительно производить, а сравнивать результаты разных замеров вообще не всегда разумно. Одна из причин: для сопоставления результатов тесты должны проходить над одними и теми же исходными данными, что, среди прочего, подразумевает воссоздание тестовой среды при каждом прогоне проверки, другая причина — сравнение субъективного восприятия времени работы тестового сценария может оказаться неточным.
          Ещё одной причиной является сложность выделения влияния на производительность целого приложения работы отдельного его модуля, того, который мы сейчас правим. Усугубляя ситуацию, уточняем: ещё сложнее это влияние вычленить, если над кодом работает коллектив из более чем одного разработчика.

          Один из подходов в такой ситуации состоит в тщательном создании полноценного тестового сценария, повторяющего работу с сервисом настоящего клиента, и прогоне его много раз, с параллельным анализом нагрузки на сервер, где проходит тестирование (таким образом, будет понятно, какая часть сценария создаёт нагрузку на отдельные ресурсы тестового сервера, что может дать дополнительную информацию по поиску мест, где следует подойти к производительности более серьёзно) — увы, не всегда можно такое позволить себе в реальной ситуации, просто потому, что объёмный тест, да ещё повторённый 10-20 раз, скорее всего будет слишком долгим, чтобы проводить его достаточно часто, а это полностью убьёт идею.

          Второй подход, более подходящий к процессу разработки, заключается в организации ограниченного по масштабу, «микро-» или даже «нано-» тестирования отдельных мест кода (скажем, запуске одного метода или одной функции, но большое число раз — т.е., скорее, бенчмаркингу). Планирование такого тестирования требует дополнительных усилий со стороны разработчика, но результат окупается и общим улучшением производительности кода, и пониманием, как ведут себя отдельные части проекта по мере работы как над ними, так и над другими частями. Вот, для примера, пара инструментов для перформанс-тестирования:

          JMH


          JMH (Java Microbenchmark Harness) — это оснастка Java для сборки, запуска и анализа нано/микро/милли/макро-бенчмарков, написанных на Java и других языках с целевой платформой JVM. Сравнительно молодой фреймворк, в котором разработчики постарались учесть все нюансы JVM. Один из удобнейших инструментов, из тех, которые приятно иметь под рукой. JMH поддерживает следующие типы замеров: Throughput (замер чистой производительности), AverageTime (замер среднего времени выполнения), SampleTime (перцентиль времени исполнения), SingleShotTime (время вызова одного метода — актуально для замера «холодного» запуска тестируемого кода).

          Поскольку речь идёт о Java, фреймворк учитывает в т.ч. и работу механизма кэширования JVM, и перед запуском бенчмарка несколько раз выполняет тестируемый код для «прогрева» кэша байт-кода Java-машины.

          BenchmarkDotNet


          BenchmarkDotNet берёт на себя рутинные действия при составлении бенчмарков для .NET-проектов и обеспечивает широкие возможности форматирования результатов ценой минимальных усилий. Как говорят авторы, фиче-реквестов хватает, поэтому BenchmarkDotNet есть куда развиваться.

          На сегодняшний день BenchmarkDotNet — библиотека, в первую очередь, для бенчмарков, а не для перфоманс-тестов. Ведётся серьёзная работа над тем, чтобы библиотеку можно было также использовать на CI-сервере для автоматического детектирования перфомансных регрессий, но пока эти разработки не завершены.

          Google Lighthouse


          Замеры производительности фронтенда всегда стояли несколько особняком: с одной стороны, часто задержки связаны со скоростью реакции бэкенда, с другой — именно по поведению фронтенда (точнее, по скорости его реакции) пользователи часто судят о всём приложении, особенно, если речь идёт про веб.

          В веб-фронтенде в отношении замеров производительности сейчас всё идёт в сторону использования Performance API и измерении именно тех параметров, которые имеют значение для конкретного проекта. Хорошим подспорьем окажется веб-сервис webpagetest.org с Performance API метками и замерами — он позволит увидеть картину не со своего компьютера, а с одной из многих точек тестирования, существующих в мире, и оценить влияние времени приёма-передачи данных через каналы интернет на работу фронтенда.

          Этот продукт больше подходил бы для проверки страниц сайта на соответствие рекомендациям Google (и вообще best practices) как для веб-сайтов, так и для Progressive Web Apps, если бы не одна из его функций: среди проверок есть и тест на поведение сайта при плохом качестве веб-соединения, а также при полном отсутствии связи. Это не очень соотносится с перформанс-тестированием как таковым, однако, если задуматься, в некоторых случаях веб-приложение воспринимается «медленным» не потому, что медленно готовит данные, а потому, что условия его работы на машине пользователя, в его браузере, с учётом его соединения с интернетом — увы, не идеальны. Google Lighthouse как раз и позволяет оценить это влияние.



          Да, тема бенчмарков и тестирования просто бесконечна. Про каждую из них можно и нужно писать пост, и не один. Однако, как мы с вами знаем, самым интересным будет не просто прочесть, а пообщаться, послушать, поспрашивать знающего человека, который, в силу своего опыта, заранее предупредит о многих мелких и крупных затруднениях, лежащих на пути освоения той или иной технологии.
          Поэтому с радостью приглашаем вас посетить конференцию Гейзенбаг 2017 Moscow, которая состоится 8-9 декабря 2017 года, где будут, в частности, представлены доклады:


          Подробности и условия участия можно узнать на сайте конференции.
          Original source: habrahabr.ru (comments, light).

          https://habrahabr.ru/post/337928/


          Метки:  

          Обзор инструментария для нагрузочного и перформанс-тестирования

          Четверг, 14 Сентября 2017 г. 13:56 + в цитатник
          ValeriaKhokha сегодня в 13:56 Разработка

          Обзор инструментария для нагрузочного и перформанс-тестирования

            Как говорят иные отважные люди: «От dev до prod — всего один шаг». Люди опытные добавляют, что шаг этот называется «тестирование», причём самое разнообразное, и нам просто нет смысла им не верить.



            Нагрузка имеет значение: водитель этого грузовика умудрился обрушить мост весом своего ТС, счёт за восстановление составил примерно 16m. К счастью, тестирование ПО обходится дешевле!

            Конечно, говоря о тестировании, нужно понять, с чем и за что мы боремся. Мы сознательно ограничили себя и решили сегодня поговорить исключительно про нагрузочное тестирование и тестирование производительности: темы, полярно удалённые друг от друга, крайне интересны в самом практическом выражении. Рассмотрим инструменты для того и другого, не привязываясь к какому-то конкретному стеку технологий, так что не удивляйтесь соседству Яндекс.Танк и BenchmarkDotNet!

            Нагрузочное тестирование


            Представим, что мы с вами написали некий сервис — теперь нужно понять, какую нагрузку он выдержит. Старая грустная шутка разработки качественного ПО гласит, что лучше иметь софт, который работает гарантированно плохо, чем софт, работающий хоть и хорошо, но негарантированно хорошо: в первом случае мы хотя бы будем знать, на что твёрдо можем рассчитывать. Далее, если наш сервис умеет масштабироваться тем или иным образом, то нужно понять, насколько с ростом нагрузки масштабирование оказывается полезным и выполняет ли оно возложенные на него проектом задачи.

            Что ж, берём и направляем на наше детище нагрузку, притом внимательно наблюдая за результатом: нас, очевидно, интересует ситуация, когда сервис либо станет отвечать на запросы с неприемлемой задержкой, либо будет возвращать неверные данные, либо вовсе перестанет подавать признаки жизни для всех запросов или лишь для их части.

            Давайте представим, что мы с вами написали некоторый сервис — для определённости скажем, что веб-сервис, но это не столь важно. Чтобы убедиться, на что мы с ним можем рассчитывать, мы начинаем «обстреливать» его запросами, наблюдая за поведением как самого сервиса, так и за нагрузкой на серверах, где он крутится. Хорошо, если заранее понятно, какие запросы нам нужно отправлять сервису (в этом случае мы можем подготовить массив запросов заранее, а после отправить его в наше приложение одним махом). Если же второй запрос зависит от результатов первого (простой пример — сначала происходит авторизация пользователя, и в следующие обращения к сервису включается информация об ID сессии), то генератор нагрузки должен быть способен генерировать тестовые запросы крайне быстро, в реальном времени.

            С учётом обстоятельств и нашего знания об объекте тестирования выбираем инструмент(ы):

            JMeter


            Да, старый добрый JMeter. Он вот уже без малого двадцать лет (!) является частым выбором для многих вариантов и типов нагрузочного тестирования: удобный GUI, независимость от платформы (спасибо Java!), поддержка многопоточности, расширяемость, отличные возможности по созданию отчётов, поддержка многих протоколов для запросов. Благодаря модульной архитектуре JMeter можно расширить в нужную пользователю сторону, реализуя даже весьма экзотические сценарии тестирования — причем, если ни один из написанных сообществом за прошедшее время плагинов нас не устроит, можно взять API и написать собственный. При необходимости с JMeter можно выстроить, хоть и ограниченное, но распределённое тестирование, когда нагрузка будет создаваться сразу несколькими машинами.

            Одной из удобных функций JMeter является работа в режиме прокси: указываем в настройках браузера в качестве прокси «127.0.0.1:8080» и посещаем браузером нужные нам страницы нужного сайта, тем временем JMeter сохраняет все наши действия и все сопутствующие запросы в виде скрипта, который позже можно будет отредактировать, как нужно — это делает процесс создания HTTP-тестов заметно проще.

            Кстати, последняя версия (3.2), вышедшая в апреле этого года, научилась отдавать результаты тестирования в InfluxDB при помощи асинхронных HTTP-запросов. Правда, начиная как раз с версии 3.2, JMeter стал требовать только Java 8, но это, наверное, не самая высокая цена за прогресс.

            Хранение тестовых сценариев у JMeter реализовано в XML-файлах, что, как оказалось, создаёт массу проблем: их совсем неудобно писать руками (читай — для создания текста необходим GUI), как неудобна и работа с такими файлами в системах управления версиями (особенно в момент, когда нужно сделать diff). Конкурирующие на поле нагрузочного тестирования продукты, такие, как Яндекс.Танк или Taurus, научились самостоятельно и на лету формировать файлы с тестами и передавать их в JMeter на исполнение, таким образом пользуясь мощью и опытом JMeter, но давая возможность пользователям создавать тесты в виде более читаемых и легче хранимых в CVS тестовых скриптов.

            LoadRunner


            Ещё один давно существующий на рынке и в определенных кругах очень известный продукт, большему распространению которого помешала принятая компанией-производителем политика лицензирования (кстати, сегодня, после слияния подразделения ПО компании Hewlett Packard Enterprise с Micro Focus International, привычное название HPE LoadRunner сменилось на Micro Focus LoadRunner). Интерес представляет логика создания теста, где несколько (наверное, правильно сказать — «много») виртуальных пользователей параллельно что-то делают с тестируемым приложением. Это даёт возможность не только оценить способность приложения обработать поток одновременных запросов, но и понять, как влияет работа одних пользователей, активно что-то делающих с сервисом, на работу других. При этом речь идёт о широком выборе протоколов взаимодействия с тестируемым приложением.

            HP в своё время создала очень хороший набор инструментов автоматизации функционального и нагрузочного тестирования, которые, при необходимости, интегрируются в процесс разработки ПО, и LoadRunner умеет интегрироваться с ними (в частности, с HP Quality Center, HP QuickTest Professional).

            Некоторое время назад производитель решил повернуться лицом к тем, кто не готов сразу платить за лицензию, и поставляет LoadRunner с бесплатной лицензией (где вписан лимит на 50 виртуальных пользователей, и запрещена небольшая часть всего набора поддерживаемых протоколов), а деньги берутся за дальнейшее расширение возможностей. Сложно сказать, насколько это будет способствовать повышению интереса к этому, без сомнения, занимательному инструменту, при наличии у него столь сильных конкурентов.

            Gatling


            Весьма мощный и серьёзный инструмент (не зря названный в честь скорострельного пулемета) — в первую очередь, по причине производительности и широты поддержки протоколов «из коробки». Например, там, где нагрузочное тестирование с JMeter будет медленным и мучительным (увы, плагин поддержки работы с веб-сокетами не особо быстр, что идейно конфликтует со скоростью работы самих веб-сокетов), Galting почти наверняка создаст нужную нагрузку без особых сложностей.

            Следует учесть, что, в отличие от JMeter, Gatling не использует GUI и вообще считается средством, ориентированным на опытную, «грамотную» аудиторию, способную создать тестовый скрипт в виде текстового файла.

            Есть у Gatling и минусы, за которые его критикуют. Во-первых, документация могла бы быть и получше, во-вторых, для работы с ним неплохо знать Scala: и сам Gatling, как инструмент тестирования, и тестовые сценарии пишутся именно на этом языке. В-третьих, разработчики «иногда» в прошлом кардинально меняли API, в результате можно было обнаружить, что тесты, написанные полугодом ранее, «не идут» на новой версии, либо требуют доработки/миграции. У Gatling также отсутствует возможность делать распределённое тестирование, что ограничивает возможные области применения.

            Яндекс.Танк


            Если коротко, Yandex Tank — это враппер над несколькими утилитами нагрузочного тестирования (включая JMeter), предоставляющий унифицированный интерфейс для их конфигурации, запуска и построения отчётов вне зависимости от того, какая утилита используется «под капотом».

            Он умеет следить за основными метриками тестируемого приложения (процессор, память, своп и пр.), за ресурсами системы (свободная память/место на диске), может остановить тест на основе разных понятных критериев («если время отклика превышает заданное значение», «если количество ошибок за единицу времени выше, чем х» и т.д). Кстати, умеет отображать в реальном времени основные статистические данные теста, что бывает очень полезно прямо в процессе теста.

            Танк используется и в самом Яндексе, и в других компаниях уже около 10 лет. Им обстреливают совершенно разные сервисы, с разными требованиями к сложности тестовых сценариев и к уровню нагрузки. Почти всегда для тестирования даже высоконагруженных сервисов хватает всего одного генератора нагрузки. Танк поддерживает разные генераторы нагрузки, как написанные специально для него (Phantom, BFG, Pandora), так и широко сторонние (JMeter). Модульная архитектура позволяет написать свой плагин под нужный генератор нагрузки и вообще прикрутить практически что угодно.

            Для чего использовать разные генераторы нагрузки? Phantom — это быстрая «пушка» на C++. Один такой генератор может выдать до сотни тысяч запросов в секунду. Но для достижения такой скорости приходится генерировать запросы заранее и нельзя (не получается) использовать получаемые от тестируемого сервиса данные для генерации очередного запроса. В случаях, когда нужно исполнять сложный сценарий или сервис использует нестандартный протокол, следует использовать JMeter, BFG, Pandora.

            В BFG, в отличие от Jmeter, нет GUI, тестовые сценарии пишутся на Python. Это позволяет использовать любые библиотеки (а их огромное количество). Часто бывает, что для сервиса написаны биндинги для Python, тогда их удобно использовать при написании нагрузочных сценариев. Pandora — это экспериментальная пушка на GoLang, достаточно быстрая и расширяемая, подходит для тестов по протоколу HTTP/2 и будет использоваться там, где нужны быстрые сценарии.

            Внутри Яндекса для хранения и отображения результатов нагрузочных тестов используется специальный сервис. Сейчас наружу открыт его упрощенный аналог под названием Overload — он полностью бесплатен, его используют, в том числе, для тестирования открытых библиотек (например) и проведения соревнований.

            Taurus


            Taurus — ещё один фреймворк над несколькими утилитами нагрузочного тестирования. Возможно, вам понравится этот продукт, использующий похожий на Яндекс.Танк подход, но имеющий несколько другой набор «фич», и, пожалуй, более адекватный формат конфигурационных файлов.

            Вообще, Taurus хорошо подойдёт в ситуации, когда мощность, скажем, Gatling важна для создания теста, но разбираться с Gatling (а также с написанием скриптов тестирования на Scala) нет желания или возможности: достаточно описать тест в куда более простом формате файла Taurus, настроить его на использование Gatling как инструмента создания нагрузки, и все Scala-файлы будут сгенерированы автоматически. Так сказать, «автоматизация автоматизации» в действии!

            Taurus можно настроить на отправку статистики теста на онлайн-сервис BlazeMeter.com, который отобразит данные в виде нарядных графиков и таблиц. Подход не очень обычный, но заслуживающий внимания: движок вывода отчётов, очевидно, совершенствуется со временем, и постепенно будет выводить информацию ещё более симпатично.

            Тестирование производительности


            Тестировать производительность сервиса или приложения можно и нужно не только после завершения процесса разработки, но и во время неё, буквально так же, как мы делаем регулярные юнит- или регрессионные тесты. Правильно организованные, регулярные тесты производительности позволяют ответить на очень «тонкий» вопрос: не привели ли последние изменения в коде приложения к ухудшению производительности получившегося ПО?

            Казалось бы, померить производительность — это так просто! Два раза взять timestamp (желательно с высокой точностью), посчитать разность, сложили-поделили, и всё — можно оптимизировать. Как бы не так! Хотя на словах этот вопрос звучит просто, на деле такого рода замеры довольно затруднительно производить, а сравнивать результаты разных замеров вообще не всегда разумно. Одна из причин: для сопоставления результатов тесты должны проходить над одними и теми же исходными данными, что, среди прочего, подразумевает воссоздание тестовой среды при каждом прогоне проверки, другая причина — сравнение субъективного восприятия времени работы тестового сценария может оказаться неточным.
            Ещё одной причиной является сложность выделения влияния на производительность целого приложения работы отдельного его модуля, того, который мы сейчас правим. Усугубляя ситуацию, уточняем: ещё сложнее это влияние вычленить, если над кодом работает коллектив из более чем одного разработчика.

            Один из подходов в такой ситуации состоит в тщательном создании полноценного тестового сценария, повторяющего работу с сервисом настоящего клиента, и прогоне его много раз, с параллельным анализом нагрузки на сервер, где проходит тестирование (таким образом, будет понятно, какая часть сценария создаёт нагрузку на отдельные ресурсы тестового сервера, что может дать дополнительную информацию по поиску мест, где следует подойти к производительности более серьёзно) — увы, не всегда можно такое позволить себе в реальной ситуации, просто потому, что объёмный тест, да ещё повторённый 10-20 раз, скорее всего будет слишком долгим, чтобы проводить его достаточно часто, а это полностью убьёт идею.

            Второй подход, более подходящий к процессу разработки, заключается в организации ограниченного по масштабу, «микро-» или даже «нано-» тестирования отдельных мест кода (скажем, запуске одного метода или одной функции, но большое число раз — т.е., скорее, бенчмаркингу). Планирование такого тестирования требует дополнительных усилий со стороны разработчика, но результат окупается и общим улучшением производительности кода, и пониманием, как ведут себя отдельные части проекта по мере работы как над ними, так и над другими частями. Вот, для примера, пара инструментов для перформанс-тестирования:

            JMH


            JMH (Java Microbenchmark Harness) — это оснастка Java для сборки, запуска и анализа нано/микро/милли/макро-бенчмарков, написанных на Java и других языках с целевой платформой JVM. Сравнительно молодой фреймворк, в котором разработчики постарались учесть все нюансы JVM. Один из удобнейших инструментов, из тех, которые приятно иметь под рукой. JMH поддерживает следующие типы замеров: Throughput (замер чистой производительности), AverageTime (замер среднего времени выполнения), SampleTime (перцентиль времени исполнения), SingleShotTime (время вызова одного метода — актуально для замера «холодного» запуска тестируемого кода).

            Поскольку речь идёт о Java, фреймворк учитывает в т.ч. и работу механизма кэширования JVM, и перед запуском бенчмарка несколько раз выполняет тестируемый код для «прогрева» кэша байт-кода Java-машины.

            BenchmarkDotNet


            BenchmarkDotNet берёт на себя рутинные действия при составлении бенчмарков для .NET-проектов и обеспечивает широкие возможности форматирования результатов ценой минимальных усилий. Как говорят авторы, фиче-реквестов хватает, поэтому BenchmarkDotNet есть куда развиваться.

            На сегодняшний день BenchmarkDotNet — библиотека, в первую очередь, для бенчмарков, а не для перфоманс-тестов. Ведётся серьёзная работа над тем, чтобы библиотеку можно было также использовать на CI-сервере для автоматического детектирования перфомансных регрессий, но пока эти разработки не завершены.

            Google Lighthouse


            Замеры производительности фронтенда всегда стояли несколько особняком: с одной стороны, часто задержки связаны со скоростью реакции бэкенда, с другой — именно по поведению фронтенда (точнее, по скорости его реакции) пользователи часто судят о всём приложении, особенно, если речь идёт про веб.

            В веб-фронтенде в отношении замеров производительности сейчас всё идёт в сторону использования Performance API и измерении именно тех параметров, которые имеют значение для конкретного проекта. Хорошим подспорьем окажется веб-сервис webpagetest.org с Performance API метками и замерами — он позволит увидеть картину не со своего компьютера, а с одной из многих точек тестирования, существующих в мире, и оценить влияние времени приёма-передачи данных через каналы интернет на работу фронтенда.

            Этот продукт больше подходил бы для проверки страниц сайта на соответствие рекомендациям Google (и вообще best practices) как для веб-сайтов, так и для Progressive Web Apps, если бы не одна из его функций: среди проверок есть и тест на поведение сайта при плохом качестве веб-соединения, а также при полном отсутствии связи. Это не очень соотносится с перформанс-тестированием как таковым, однако, если задуматься, в некоторых случаях веб-приложение воспринимается «медленным» не потому, что медленно готовит данные, а потому, что условия его работы на машине пользователя, в его браузере, с учётом его соединения с интернетом — увы, не идеальны. Google Lighthouse как раз и позволяет оценить это влияние.



            Да, тема бенчмарков и тестирования просто бесконечна. Про каждую из них можно и нужно писать пост, и не один. Однако, как мы с вами знаем, самым интересным будет не просто прочесть, а пообщаться, послушать, поспрашивать знающего человека, который, в силу своего опыта, заранее предупредит о многих мелких и крупных затруднениях, лежащих на пути освоения той или иной технологии.
            Поэтому с радостью приглашаем вас посетить конференцию Гейзенбаг 2017 Moscow, которая состоится 8-9 декабря 2017 года, где будут, в частности, представлены доклады:


            Подробности и условия участия можно узнать на сайте конференции.
            Original source: habrahabr.ru (comments, light).

            https://habrahabr.ru/post/337928/


            Метки:  

            Обзор инструментария для нагрузочного и перформанс-тестирования

            Четверг, 14 Сентября 2017 г. 13:56 + в цитатник
            ValeriaKhokha сегодня в 13:56 Разработка

            Обзор инструментария для нагрузочного и перформанс-тестирования

              Как говорят иные отважные люди: «От dev до prod — всего один шаг». Люди опытные добавляют, что шаг этот называется «тестирование», причём самое разнообразное, и нам просто нет смысла им не верить.



              Нагрузка имеет значение: водитель этого грузовика умудрился обрушить мост весом своего ТС, счёт за восстановление составил примерно 16m. К счастью, тестирование ПО обходится дешевле!

              Конечно, говоря о тестировании, нужно понять, с чем и за что мы боремся. Мы сознательно ограничили себя и решили сегодня поговорить исключительно про нагрузочное тестирование и тестирование производительности: темы, полярно удалённые друг от друга, крайне интересны в самом практическом выражении. Рассмотрим инструменты для того и другого, не привязываясь к какому-то конкретному стеку технологий, так что не удивляйтесь соседству Яндекс.Танк и BenchmarkDotNet!

              Нагрузочное тестирование


              Представим, что мы с вами написали некий сервис — теперь нужно понять, какую нагрузку он выдержит. Старая грустная шутка разработки качественного ПО гласит, что лучше иметь софт, который работает гарантированно плохо, чем софт, работающий хоть и хорошо, но негарантированно хорошо: в первом случае мы хотя бы будем знать, на что твёрдо можем рассчитывать. Далее, если наш сервис умеет масштабироваться тем или иным образом, то нужно понять, насколько с ростом нагрузки масштабирование оказывается полезным и выполняет ли оно возложенные на него проектом задачи.

              Что ж, берём и направляем на наше детище нагрузку, притом внимательно наблюдая за результатом: нас, очевидно, интересует ситуация, когда сервис либо станет отвечать на запросы с неприемлемой задержкой, либо будет возвращать неверные данные, либо вовсе перестанет подавать признаки жизни для всех запросов или лишь для их части.

              Давайте представим, что мы с вами написали некоторый сервис — для определённости скажем, что веб-сервис, но это не столь важно. Чтобы убедиться, на что мы с ним можем рассчитывать, мы начинаем «обстреливать» его запросами, наблюдая за поведением как самого сервиса, так и за нагрузкой на серверах, где он крутится. Хорошо, если заранее понятно, какие запросы нам нужно отправлять сервису (в этом случае мы можем подготовить массив запросов заранее, а после отправить его в наше приложение одним махом). Если же второй запрос зависит от результатов первого (простой пример — сначала происходит авторизация пользователя, и в следующие обращения к сервису включается информация об ID сессии), то генератор нагрузки должен быть способен генерировать тестовые запросы крайне быстро, в реальном времени.

              С учётом обстоятельств и нашего знания об объекте тестирования выбираем инструмент(ы):

              JMeter


              Да, старый добрый JMeter. Он вот уже без малого двадцать лет (!) является частым выбором для многих вариантов и типов нагрузочного тестирования: удобный GUI, независимость от платформы (спасибо Java!), поддержка многопоточности, расширяемость, отличные возможности по созданию отчётов, поддержка многих протоколов для запросов. Благодаря модульной архитектуре JMeter можно расширить в нужную пользователю сторону, реализуя даже весьма экзотические сценарии тестирования — причем, если ни один из написанных сообществом за прошедшее время плагинов нас не устроит, можно взять API и написать собственный. При необходимости с JMeter можно выстроить, хоть и ограниченное, но распределённое тестирование, когда нагрузка будет создаваться сразу несколькими машинами.

              Одной из удобных функций JMeter является работа в режиме прокси: указываем в настройках браузера в качестве прокси «127.0.0.1:8080» и посещаем браузером нужные нам страницы нужного сайта, тем временем JMeter сохраняет все наши действия и все сопутствующие запросы в виде скрипта, который позже можно будет отредактировать, как нужно — это делает процесс создания HTTP-тестов заметно проще.

              Кстати, последняя версия (3.2), вышедшая в апреле этого года, научилась отдавать результаты тестирования в InfluxDB при помощи асинхронных HTTP-запросов. Правда, начиная как раз с версии 3.2, JMeter стал требовать только Java 8, но это, наверное, не самая высокая цена за прогресс.

              Хранение тестовых сценариев у JMeter реализовано в XML-файлах, что, как оказалось, создаёт массу проблем: их совсем неудобно писать руками (читай — для создания текста необходим GUI), как неудобна и работа с такими файлами в системах управления версиями (особенно в момент, когда нужно сделать diff). Конкурирующие на поле нагрузочного тестирования продукты, такие, как Яндекс.Танк или Taurus, научились самостоятельно и на лету формировать файлы с тестами и передавать их в JMeter на исполнение, таким образом пользуясь мощью и опытом JMeter, но давая возможность пользователям создавать тесты в виде более читаемых и легче хранимых в CVS тестовых скриптов.

              LoadRunner


              Ещё один давно существующий на рынке и в определенных кругах очень известный продукт, большему распространению которого помешала принятая компанией-производителем политика лицензирования (кстати, сегодня, после слияния подразделения ПО компании Hewlett Packard Enterprise с Micro Focus International, привычное название HPE LoadRunner сменилось на Micro Focus LoadRunner). Интерес представляет логика создания теста, где несколько (наверное, правильно сказать — «много») виртуальных пользователей параллельно что-то делают с тестируемым приложением. Это даёт возможность не только оценить способность приложения обработать поток одновременных запросов, но и понять, как влияет работа одних пользователей, активно что-то делающих с сервисом, на работу других. При этом речь идёт о широком выборе протоколов взаимодействия с тестируемым приложением.

              HP в своё время создала очень хороший набор инструментов автоматизации функционального и нагрузочного тестирования, которые, при необходимости, интегрируются в процесс разработки ПО, и LoadRunner умеет интегрироваться с ними (в частности, с HP Quality Center, HP QuickTest Professional).

              Некоторое время назад производитель решил повернуться лицом к тем, кто не готов сразу платить за лицензию, и поставляет LoadRunner с бесплатной лицензией (где вписан лимит на 50 виртуальных пользователей, и запрещена небольшая часть всего набора поддерживаемых протоколов), а деньги берутся за дальнейшее расширение возможностей. Сложно сказать, насколько это будет способствовать повышению интереса к этому, без сомнения, занимательному инструменту, при наличии у него столь сильных конкурентов.

              Gatling


              Весьма мощный и серьёзный инструмент (не зря названный в честь скорострельного пулемета) — в первую очередь, по причине производительности и широты поддержки протоколов «из коробки». Например, там, где нагрузочное тестирование с JMeter будет медленным и мучительным (увы, плагин поддержки работы с веб-сокетами не особо быстр, что идейно конфликтует со скоростью работы самих веб-сокетов), Galting почти наверняка создаст нужную нагрузку без особых сложностей.

              Следует учесть, что, в отличие от JMeter, Gatling не использует GUI и вообще считается средством, ориентированным на опытную, «грамотную» аудиторию, способную создать тестовый скрипт в виде текстового файла.

              Есть у Gatling и минусы, за которые его критикуют. Во-первых, документация могла бы быть и получше, во-вторых, для работы с ним неплохо знать Scala: и сам Gatling, как инструмент тестирования, и тестовые сценарии пишутся именно на этом языке. В-третьих, разработчики «иногда» в прошлом кардинально меняли API, в результате можно было обнаружить, что тесты, написанные полугодом ранее, «не идут» на новой версии, либо требуют доработки/миграции. У Gatling также отсутствует возможность делать распределённое тестирование, что ограничивает возможные области применения.

              Яндекс.Танк


              Если коротко, Yandex Tank — это враппер над несколькими утилитами нагрузочного тестирования (включая JMeter), предоставляющий унифицированный интерфейс для их конфигурации, запуска и построения отчётов вне зависимости от того, какая утилита используется «под капотом».

              Он умеет следить за основными метриками тестируемого приложения (процессор, память, своп и пр.), за ресурсами системы (свободная память/место на диске), может остановить тест на основе разных понятных критериев («если время отклика превышает заданное значение», «если количество ошибок за единицу времени выше, чем х» и т.д). Кстати, умеет отображать в реальном времени основные статистические данные теста, что бывает очень полезно прямо в процессе теста.

              Танк используется и в самом Яндексе, и в других компаниях уже около 10 лет. Им обстреливают совершенно разные сервисы, с разными требованиями к сложности тестовых сценариев и к уровню нагрузки. Почти всегда для тестирования даже высоконагруженных сервисов хватает всего одного генератора нагрузки. Танк поддерживает разные генераторы нагрузки, как написанные специально для него (Phantom, BFG, Pandora), так и широко сторонние (JMeter). Модульная архитектура позволяет написать свой плагин под нужный генератор нагрузки и вообще прикрутить практически что угодно.

              Для чего использовать разные генераторы нагрузки? Phantom — это быстрая «пушка» на C++. Один такой генератор может выдать до сотни тысяч запросов в секунду. Но для достижения такой скорости приходится генерировать запросы заранее и нельзя (не получается) использовать получаемые от тестируемого сервиса данные для генерации очередного запроса. В случаях, когда нужно исполнять сложный сценарий или сервис использует нестандартный протокол, следует использовать JMeter, BFG, Pandora.

              В BFG, в отличие от Jmeter, нет GUI, тестовые сценарии пишутся на Python. Это позволяет использовать любые библиотеки (а их огромное количество). Часто бывает, что для сервиса написаны биндинги для Python, тогда их удобно использовать при написании нагрузочных сценариев. Pandora — это экспериментальная пушка на GoLang, достаточно быстрая и расширяемая, подходит для тестов по протоколу HTTP/2 и будет использоваться там, где нужны быстрые сценарии.

              Внутри Яндекса для хранения и отображения результатов нагрузочных тестов используется специальный сервис. Сейчас наружу открыт его упрощенный аналог под названием Overload — он полностью бесплатен, его используют, в том числе, для тестирования открытых библиотек (например) и проведения соревнований.

              Taurus


              Taurus — ещё один фреймворк над несколькими утилитами нагрузочного тестирования. Возможно, вам понравится этот продукт, использующий похожий на Яндекс.Танк подход, но имеющий несколько другой набор «фич», и, пожалуй, более адекватный формат конфигурационных файлов.

              Вообще, Taurus хорошо подойдёт в ситуации, когда мощность, скажем, Gatling важна для создания теста, но разбираться с Gatling (а также с написанием скриптов тестирования на Scala) нет желания или возможности: достаточно описать тест в куда более простом формате файла Taurus, настроить его на использование Gatling как инструмента создания нагрузки, и все Scala-файлы будут сгенерированы автоматически. Так сказать, «автоматизация автоматизации» в действии!

              Taurus можно настроить на отправку статистики теста на онлайн-сервис BlazeMeter.com, который отобразит данные в виде нарядных графиков и таблиц. Подход не очень обычный, но заслуживающий внимания: движок вывода отчётов, очевидно, совершенствуется со временем, и постепенно будет выводить информацию ещё более симпатично.

              Тестирование производительности


              Тестировать производительность сервиса или приложения можно и нужно не только после завершения процесса разработки, но и во время неё, буквально так же, как мы делаем регулярные юнит- или регрессионные тесты. Правильно организованные, регулярные тесты производительности позволяют ответить на очень «тонкий» вопрос: не привели ли последние изменения в коде приложения к ухудшению производительности получившегося ПО?

              Казалось бы, померить производительность — это так просто! Два раза взять timestamp (желательно с высокой точностью), посчитать разность, сложили-поделили, и всё — можно оптимизировать. Как бы не так! Хотя на словах этот вопрос звучит просто, на деле такого рода замеры довольно затруднительно производить, а сравнивать результаты разных замеров вообще не всегда разумно. Одна из причин: для сопоставления результатов тесты должны проходить над одними и теми же исходными данными, что, среди прочего, подразумевает воссоздание тестовой среды при каждом прогоне проверки, другая причина — сравнение субъективного восприятия времени работы тестового сценария может оказаться неточным.
              Ещё одной причиной является сложность выделения влияния на производительность целого приложения работы отдельного его модуля, того, который мы сейчас правим. Усугубляя ситуацию, уточняем: ещё сложнее это влияние вычленить, если над кодом работает коллектив из более чем одного разработчика.

              Один из подходов в такой ситуации состоит в тщательном создании полноценного тестового сценария, повторяющего работу с сервисом настоящего клиента, и прогоне его много раз, с параллельным анализом нагрузки на сервер, где проходит тестирование (таким образом, будет понятно, какая часть сценария создаёт нагрузку на отдельные ресурсы тестового сервера, что может дать дополнительную информацию по поиску мест, где следует подойти к производительности более серьёзно) — увы, не всегда можно такое позволить себе в реальной ситуации, просто потому, что объёмный тест, да ещё повторённый 10-20 раз, скорее всего будет слишком долгим, чтобы проводить его достаточно часто, а это полностью убьёт идею.

              Второй подход, более подходящий к процессу разработки, заключается в организации ограниченного по масштабу, «микро-» или даже «нано-» тестирования отдельных мест кода (скажем, запуске одного метода или одной функции, но большое число раз — т.е., скорее, бенчмаркингу). Планирование такого тестирования требует дополнительных усилий со стороны разработчика, но результат окупается и общим улучшением производительности кода, и пониманием, как ведут себя отдельные части проекта по мере работы как над ними, так и над другими частями. Вот, для примера, пара инструментов для перформанс-тестирования:

              JMH


              JMH (Java Microbenchmark Harness) — это оснастка Java для сборки, запуска и анализа нано/микро/милли/макро-бенчмарков, написанных на Java и других языках с целевой платформой JVM. Сравнительно молодой фреймворк, в котором разработчики постарались учесть все нюансы JVM. Один из удобнейших инструментов, из тех, которые приятно иметь под рукой. JMH поддерживает следующие типы замеров: Throughput (замер чистой производительности), AverageTime (замер среднего времени выполнения), SampleTime (перцентиль времени исполнения), SingleShotTime (время вызова одного метода — актуально для замера «холодного» запуска тестируемого кода).

              Поскольку речь идёт о Java, фреймворк учитывает в т.ч. и работу механизма кэширования JVM, и перед запуском бенчмарка несколько раз выполняет тестируемый код для «прогрева» кэша байт-кода Java-машины.

              BenchmarkDotNet


              BenchmarkDotNet берёт на себя рутинные действия при составлении бенчмарков для .NET-проектов и обеспечивает широкие возможности форматирования результатов ценой минимальных усилий. Как говорят авторы, фиче-реквестов хватает, поэтому BenchmarkDotNet есть куда развиваться.

              На сегодняшний день BenchmarkDotNet — библиотека, в первую очередь, для бенчмарков, а не для перфоманс-тестов. Ведётся серьёзная работа над тем, чтобы библиотеку можно было также использовать на CI-сервере для автоматического детектирования перфомансных регрессий, но пока эти разработки не завершены.

              Google Lighthouse


              Замеры производительности фронтенда всегда стояли несколько особняком: с одной стороны, часто задержки связаны со скоростью реакции бэкенда, с другой — именно по поведению фронтенда (точнее, по скорости его реакции) пользователи часто судят о всём приложении, особенно, если речь идёт про веб.

              В веб-фронтенде в отношении замеров производительности сейчас всё идёт в сторону использования Performance API и измерении именно тех параметров, которые имеют значение для конкретного проекта. Хорошим подспорьем окажется веб-сервис webpagetest.org с Performance API метками и замерами — он позволит увидеть картину не со своего компьютера, а с одной из многих точек тестирования, существующих в мире, и оценить влияние времени приёма-передачи данных через каналы интернет на работу фронтенда.

              Этот продукт больше подходил бы для проверки страниц сайта на соответствие рекомендациям Google (и вообще best practices) как для веб-сайтов, так и для Progressive Web Apps, если бы не одна из его функций: среди проверок есть и тест на поведение сайта при плохом качестве веб-соединения, а также при полном отсутствии связи. Это не очень соотносится с перформанс-тестированием как таковым, однако, если задуматься, в некоторых случаях веб-приложение воспринимается «медленным» не потому, что медленно готовит данные, а потому, что условия его работы на машине пользователя, в его браузере, с учётом его соединения с интернетом — увы, не идеальны. Google Lighthouse как раз и позволяет оценить это влияние.



              Да, тема бенчмарков и тестирования просто бесконечна. Про каждую из них можно и нужно писать пост, и не один. Однако, как мы с вами знаем, самым интересным будет не просто прочесть, а пообщаться, послушать, поспрашивать знающего человека, который, в силу своего опыта, заранее предупредит о многих мелких и крупных затруднениях, лежащих на пути освоения той или иной технологии.
              Поэтому с радостью приглашаем вас посетить конференцию Гейзенбаг 2017 Moscow, которая состоится 8-9 декабря 2017 года, где будут, в частности, представлены доклады:


              Подробности и условия участия можно узнать на сайте конференции.
              Original source: habrahabr.ru (comments, light).

              https://habrahabr.ru/post/337928/


              Метки:  

              Корпоративные лаборатории: выявление инцидентов информационной безопасности

              Четверг, 14 Сентября 2017 г. 13:37 + в цитатник

              Метки:  

              Корпоративные лаборатории: выявление инцидентов информационной безопасности

              Четверг, 14 Сентября 2017 г. 13:37 + в цитатник

              Метки:  

              Корпоративные лаборатории: выявление инцидентов информационной безопасности

              Четверг, 14 Сентября 2017 г. 13:37 + в цитатник

              Метки:  

              [Из песочницы] Начальник, хочу работать из дома

              Четверг, 14 Сентября 2017 г. 13:35 + в цитатник
              digore сегодня в 13:35 Управление

              Начальник, хочу работать из дома

              Несколько лет назад примерно с такой фразой ко мне пришел один из ведущих программистов. Давайте назовем его Иваном (все персонажи вымышленные, а любые совпадения случайны). Он собирался переехать жить за город и каждый день ездить в офис ему стало неудобно. У Ивана был приличный послужной список, несколько лет работы в компании и большой вагон доверия. Тогда мы договорились, что Иван будет работать удаленно понедельник и пятницу, а вторник-четверг он приезжает в офис с утренним графиком.

              Небольшая ремарка об авторе
              Автор работает в большой компании разработки ПО в Нижнем Новгороде с обычным для IT гибким графиком. Постоянные удаленные сотрудники – это скорее из области виртуальной реальности.

              Я понимал, что данное решение никак не повлияет на производительность и результаты работы Ивана, все знали, насколько он крут. С одной стороны, сотрудник получил возможность работать из дома 2 дня в неделю и сильно смещенный к утру график в остальные дни. С другой стороны, мы договорились о бонусе, который удерживает ведущего специалиста в нашем отделе от перехода куда-либо. Win-win, как ни крути!

              Через некоторое время, когда мы искали в команду Ивана крутого программиста, возможность поработать два дня удаленно сыграла для нового человека Василия решающую роль в принятии предложения о работе.

              Тогда я задумался, почему остальные команды разработки не могут также работать удаленно. Чем они отличаются от Ивана и Василия?

              Но для руководства в компании всегда проще, чтобы люди сидели в офисе и были под контролем. Большинство страхов к удаленной работе вызвано недоверием к подчиненным:

              • А вдруг они перестанут работать?
              • А вдруг мне что-то резко понадобится от сотрудника и я не смогу найти его на месте?

              Можно подумать, если сотрудник будет в офисе, он не найдет возможности пинать балду и смотреть котиков. Большинство аргументов против удаленной работы основаны на страхе потерять контроль, давайте говорить об этом прямо. У меня не было проблем с доверием к людям, которых нанимал на работу.

              Спойлер
              Да, конечно, были люди, которые нарушали правила и теряли доверие. С такими людьми мы расставались по обоюдному согласию. Но это совсем другая история.

              Посмотрим, какие бонусы получают сотрудники от удаленной работы:

              • Возможность не тратить на дорогу до работы драгоценное время. Конечно, у нас не Москва, но пробки большие. Про стресс, который приносит общественный транспорт, можно также много говорить. Все это сказывается на продуктивности и здоровье сотрудников.
              • Возможность работать в комфортных условиях. Согласитесь, мало кто на вопрос «где вам хорошо работается» ответит «в офисе». Или уточнит про раннее утро, когда никого нет. Не все понимают, что работа программиста – это творческая работа, которая требует сосредоточенности и концентрации внимания.

              Таким образом, у меня не было внутренних противоречий против удаленной работы. Сперва я поговорил с руководителями групп разработки и тестирования и получил от них восторженный аппрув по предложению удаленной работы. Оставалось самое сложное – убедить руководство.

              Моими доводами в этом стали:

              1. Иван с Василием работают из дома, почему остальным нельзя? Мы же команда, делающая общее дело!
              2. Давай судить по результату работы, а не по тому месту, где был достигнут результат.
              3. Дополнительная нематериальная мотивация, которая для многих сотрудников может стать решающей в решении уйти или остаться.

              Нужно отдать должное моему боссу, он во многих вопросах поддержал меня, так что на первое время мы договорились с ним на один удаленный день для всех сотрудников подразделения. Победа? – Да, маленькая, но победа!

              Я решил двигаться маленькими шажками: удаленный день для некоторых сотрудников -> удаленный день для всех сотрудников -> 2 удаленных дня для всех сотрудников -> ???
              Основная идея: доказать, что удаленная работа не вредит бизнесу, а только помогает ему.

              Итого, текущий результат, возможность работать понедельник и пятницу удаленно для всех сотрудников подразделения.

              • Сотрудники получают возможность в четверг вечером уехать куда-то за город и вернуться в город только в понедельник вечером. Заметьте, без пробок!
              • Сотрудники сами могут выбрать свой рабочий график.
              • Производительность труда нисколько не просела.
              • Мотивация и общий эмоциональный фон улучшились.
              • На общение с коллегами с глазу на глаз и проведение собраний остается 3 дня. Этого достаточно, чтобы не потерять себя для социума.

              А как вы работаете в больших компаниях? Есть ли у вас удаленные дни и возможность поработать из дома?

              Фрилансеров, работающих полностью удаленно, призываю также оставить комментарии.

              Напоследок, приведу всем сомневающимся отличную книгу про плюсы удаленной работы «Remote: офис не обязателен» Джейсон Фрайд, Дэвид Хенссон.
              Original source: habrahabr.ru (comments, light).

              https://habrahabr.ru/post/337936/


              Метки:  

              [Из песочницы] Начальник, хочу работать из дома

              Четверг, 14 Сентября 2017 г. 13:35 + в цитатник
              digore сегодня в 13:35 Управление

              Начальник, хочу работать из дома

              Несколько лет назад примерно с такой фразой ко мне пришел один из ведущих программистов. Давайте назовем его Иваном (все персонажи вымышленные, а любые совпадения случайны). Он собирался переехать жить за город и каждый день ездить в офис ему стало неудобно. У Ивана был приличный послужной список, несколько лет работы в компании и большой вагон доверия. Тогда мы договорились, что Иван будет работать удаленно понедельник и пятницу, а вторник-четверг он приезжает в офис с утренним графиком.

              Небольшая ремарка об авторе
              Автор работает в большой компании разработки ПО в Нижнем Новгороде с обычным для IT гибким графиком. Постоянные удаленные сотрудники – это скорее из области виртуальной реальности.

              Я понимал, что данное решение никак не повлияет на производительность и результаты работы Ивана, все знали, насколько он крут. С одной стороны, сотрудник получил возможность работать из дома 2 дня в неделю и сильно смещенный к утру график в остальные дни. С другой стороны, мы договорились о бонусе, который удерживает ведущего специалиста в нашем отделе от перехода куда-либо. Win-win, как ни крути!

              Через некоторое время, когда мы искали в команду Ивана крутого программиста, возможность поработать два дня удаленно сыграла для нового человека Василия решающую роль в принятии предложения о работе.

              Тогда я задумался, почему остальные команды разработки не могут также работать удаленно. Чем они отличаются от Ивана и Василия?

              Но для руководства в компании всегда проще, чтобы люди сидели в офисе и были под контролем. Большинство страхов к удаленной работе вызвано недоверием к подчиненным:

              • А вдруг они перестанут работать?
              • А вдруг мне что-то резко понадобится от сотрудника и я не смогу найти его на месте?

              Можно подумать, если сотрудник будет в офисе, он не найдет возможности пинать балду и смотреть котиков. Большинство аргументов против удаленной работы основаны на страхе потерять контроль, давайте говорить об этом прямо. У меня не было проблем с доверием к людям, которых нанимал на работу.

              Спойлер
              Да, конечно, были люди, которые нарушали правила и теряли доверие. С такими людьми мы расставались по обоюдному согласию. Но это совсем другая история.

              Посмотрим, какие бонусы получают сотрудники от удаленной работы:

              • Возможность не тратить на дорогу до работы драгоценное время. Конечно, у нас не Москва, но пробки большие. Про стресс, который приносит общественный транспорт, можно также много говорить. Все это сказывается на продуктивности и здоровье сотрудников.
              • Возможность работать в комфортных условиях. Согласитесь, мало кто на вопрос «где вам хорошо работается» ответит «в офисе». Или уточнит про раннее утро, когда никого нет. Не все понимают, что работа программиста – это творческая работа, которая требует сосредоточенности и концентрации внимания.

              Таким образом, у меня не было внутренних противоречий против удаленной работы. Сперва я поговорил с руководителями групп разработки и тестирования и получил от них восторженный аппрув по предложению удаленной работы. Оставалось самое сложное – убедить руководство.

              Моими доводами в этом стали:

              1. Иван с Василием работают из дома, почему остальным нельзя? Мы же команда, делающая общее дело!
              2. Давай судить по результату работы, а не по тому месту, где был достигнут результат.
              3. Дополнительная нематериальная мотивация, которая для многих сотрудников может стать решающей в решении уйти или остаться.

              Нужно отдать должное моему боссу, он во многих вопросах поддержал меня, так что на первое время мы договорились с ним на один удаленный день для всех сотрудников подразделения. Победа? – Да, маленькая, но победа!

              Я решил двигаться маленькими шажками: удаленный день для некоторых сотрудников -> удаленный день для всех сотрудников -> 2 удаленных дня для всех сотрудников -> ???
              Основная идея: доказать, что удаленная работа не вредит бизнесу, а только помогает ему.

              Итого, текущий результат, возможность работать понедельник и пятницу удаленно для всех сотрудников подразделения.

              • Сотрудники получают возможность в четверг вечером уехать куда-то за город и вернуться в город только в понедельник вечером. Заметьте, без пробок!
              • Сотрудники сами могут выбрать свой рабочий график.
              • Производительность труда нисколько не просела.
              • Мотивация и общий эмоциональный фон улучшились.
              • На общение с коллегами с глазу на глаз и проведение собраний остается 3 дня. Этого достаточно, чтобы не потерять себя для социума.

              А как вы работаете в больших компаниях? Есть ли у вас удаленные дни и возможность поработать из дома?

              Фрилансеров, работающих полностью удаленно, призываю также оставить комментарии.

              Напоследок, приведу всем сомневающимся отличную книгу про плюсы удаленной работы «Remote: офис не обязателен» Джейсон Фрайд, Дэвид Хенссон.
              Original source: habrahabr.ru (comments, light).

              https://habrahabr.ru/post/337936/


              Метки:  

              Поиск сообщений в rss_rss_hh_full
              Страницы: 1824 ... 1531 1530 [1529] 1528 1527 ..
              .. 1 Календарь