WCF - ключ к любым .Net технологиям в шаблоне. Нейросеть Microsoft.ML.Net в ваших проектах

evgen2208

Client
Регистрация
12.10.2016
Сообщения
126
Благодарностей
148
Баллы
43
Дисклеймер:
Первоначальная статья получилась настолько бомбической, что даже не прошла модерацию. Так что, итогового готового решения не будет, но идеи, изложенные в этом материале могут ОЧЕНЬ облегчить жизнь юным автоматизаторам.


Поехали!
Сколько раз вы выдели крутые проекты для гитхабе, или примеры работы какой-то нужной библиотеки,
написанные для visual studio, но сталкивались с тем, что абсолютно непонятно, как это использовать в зенопостере?
Я вот сталкивался. Да и кроме того, писать код в VS или VSCode в разы приятнее чем в редакторе PM (я пользуюсь 5й версией. может редактор уже и поменялся).

К счастью, почти любой найденный декстоп-проект можно сократить до консольного приложения, выбросив оттуда всё ненужное. (Удалять из проекта ненужное тоже надо уметь к сожалению, но это намного проще чем писать код).
И к счастью, есть такая технология Windows Communiction Foundation (WCF) с помощью которой мы можем писать распределенные приложения: В одном месте "сервер" с логикой, в совершенно другом приложении - "клиент" в котором мы подключаемся к "серверу" и вызываем его открытые методы. Технология эта сетевая, но мы будем использовать её на localhost, чтобы не создавать дополнительных трудностей, которые придется решать.

Начнем с простого примера:
Вот здесь https://alekseygulynin.ru/primer-wcf/ описан пример создания простого калькулятора.

Повторим его почти полностью.
В vs напишем "серверную часть" а в зенопостере "клиент".
Если не установлено - Качаем visual studio 2019 https://visualstudio.microsoft.com/ru/thank-you-downloading-visual-studio/?sku=Community&rel=16
бесплатную community версию.

Создаем консольное приложение
1.png

Обязательно под платформу .NetFramwork, версия не особо важна, главное не .NetCore.
И полностью копипастим верхний фрагмент кода из статьи в файл Program.cs

C#:
  [ServiceContract]
using System;
using System.Text;
// Обратите внимание, данную библиотеку нужно будет подключить
using System.ServiceModel;
namespace Server
{
  [ServiceContract]
  public interface IMyService
  {
    // Далее идут 2 метода, которые будем запрашивать у службы
    // Просто опишем их, реализовывать будем в классе
    // Сложение
    [OperationContract]
    double GetSum(double i, double j);
    // Умножение
    [OperationContract]
    double GetMult(double i, double j);
  }
  // Реализация методов, которые описаны в интерфейсе
  public class MyService : IMyService
  {
    public double GetSum(double i, double j)
    {
      return i + j;
    }

    public double GetMult(double i, double j)
    {
      return i * j;
    }
  }
  class Program
  {
    static void Main(string[] args)
    {
      // Инициализируем службу, указываем адрес, по которому она будет доступна
      ServiceHost host = new ServiceHost(typeof(MyService), new Uri("http://localhost:8000/MyService"));
      // Добавляем конечную точку службы с заданным интерфейсом, привязкой (создаём новую) и адресом конечной точки
      host.AddServiceEndpoint(typeof(IMyService), new BasicHttpBinding(), "");
      // Запускаем службу
      host.Open();
      Console.WriteLine("Сервер запущен");
      Console.ReadLine();
      // Закрываем службу
      host.Close();
    }
  }
}
Все что красное - наводим мышью и выбираем нужную подсказку:
2.png


Умная студия сама подтянет все зависимости.

От себя добавим только две строчки, чтобы в консоли выводилась инфа о том методе который вызывается.
перепишем немного класс MyService:
C#:
    // Реализация методов, которые описаны в интерфейсе
    public class MyService : IMyService
    {
        public double GetSum(double i, double j)
        {
            Console.WriteLine($"GetSum {i} + {j}");
            return i + j;
        }

        public double GetMult(double i, double j)
        {
            Console.WriteLine($"GetMult {i} * {j}");
            return i * j;
        }
    }
Скомпилируем (Сборка - Собрать решение или CTRL + SHIFT + B)
И запустим проект: запускать нужно не из студии, а в в папке "..\bin\Debug" .exe файл, обязательно "от имени администратора"
(у меня C:\Users\evgen\Documents\ZennoLab\12й конкурс\на экспорт\калькулятор\Calc12\bin\Debug\Calc12.exe)


Студию можно пока закрыть, а консольку оставляем:
3.png


Теперь у нас по адресу (если ничего не меняли из кода в статье) доступны вызовы методов описанного интерфейса:

C#:
double GetSum(double i, double j); //сумма
double GetMult(double i, double j); //умножение
Напишем под них "клиента" в PM:

Создаем шаблон, добавляем ссылку из GAC
5.png
.

В общем коде добавляем Using и интерфейс:
6.png
.
А следом немного своего кода:

C#:
    public class Service
    {
        IMyService instance;
 
        public Service()
        {
                Uri tcpUri = new Uri("http://localhost:8000/MyService");
              // Создаём сетевой адрес, с которым клиент будет взаимодействовать
                EndpointAddress address = new EndpointAddress(tcpUri);
                BasicHttpBinding binding = new BasicHttpBinding();
              // Данный класс используется клиентами для отправки сообщений
                ChannelFactory<IMyService> factory = new ChannelFactory<IMyService>(binding, address);
              // Открываем канал для общения клиента с со службой
                instance = factory.CreateChannel();
        }
 
        public IMyService GetInstance()
        {
            return instance;
        }
    }
Добавляем C#-кубик и пишем там такой код:

C#:
var service = new OwnCode.Service().GetInstance(); //получаем экземпляр сервиса
var summ = service.GetSum(5,4); // вызываем метод СУММА
project.SendInfoToLog("5+4 = "+summ.ToString());

var mult = service.GetMult(11,5); // вызываем метод УМНОЖЕНИЕ
project.SendInfoToLog("11*5 = "+mult.ToString());
Запускаем, и в логе зеннопостера видим следующее:
7.png
,
А в нашей открытой консольке:
8.png

(я несколько раз запускал шаблон, поэтому здесь большее количество вызовов. Картинке с логом ZP соответствуют только последние две строчки из консоли).

Вся магия в том, что мы отправляем две цифры из Зенопостера, а саму арифметику считает консольное приложение.
В классе Service общего кода мы устанавливаем соединение с нашим сервисом, а в кубике пользуемся этим сервисом.

Соответственно, логика на "сервере" может быть любого уровня сложности. И мы можем вынести её из шаблона ZP и писать код в нормальной среде VisualStudio 2019.



Для закрепления первого этапа, напишем свой CRUD-интерфейс для работы с БД.
У меня локально установлена только БД ПОСТГРЕС, с ней и поработаем c использованием Dapper.

У меня есть некоторая таблица с товарами:
9.png


Напишем в VS для работы с ней Интерфейс, реализуем его и напишем шаблон в ZP для работы с этой таблицей:

Делаем всё то же самое, как и раньше:
Создаем консольное приложение в VS, устанавливаем пакеты Dapper и Npgsql через консоль:
10.png


install-package npgsql
install-package Dapper

Далее создадим класс Item описывающий элемент в таблице Goods (забегая вперед, скажу что я столкнулся с трудностью сериализации данных на стороне ZP,
поэтому пришлось использовать XmlSerializer, и у нас добавились некоторые атрибуты)
C#:
    public class Item
    {
        [XmlElement]
        public int Id { get; set; }
        [XmlElement]
        public string Name { get; set; }
        [XmlElement]
        public string Description { get; set; }
        [XmlElement]
        public decimal Price { get; set; }
    }
Далее опишем вот такой интерфейс:
C#:
    [ServiceContract]
    [XmlSerializerFormat]
    public interface IDapperService
    {
        [OperationContract]
        void Add(Item item); //создание
        [OperationContract]
        void Remove(int id); // удаление
        [OperationContract]
        void Update(Item item); // обнвление
        [OperationContract]
        Item FindByID(int id); //чтение 1 записи по ключу
        [OperationContract]
        Item[] FindAll(); //чтение всех записей
    }
и реализуем этот интерфейс. Кода получилось много:

C#:
    public class DapperService : IDapperService
    {
        private string connectionString = "User ID=postgres;Password=******;Host=localhost;Port=5433;Database=postgres;";
        internal IDbConnection Connection
        {
            get { return new NpgsqlConnection(connectionString); }
        }

        public void Add(Item item)
        {
            Console.WriteLine("Execute method Add");
            using (IDbConnection conn = Connection)
            {
                conn.Open();
                string command = "insert into \"Goods\"(\"Name\",\"Description\",\"Price\") ";
                command += "values(@Name,@Description,@Price)";
                conn.Execute(command, item);
            }
        }

        public Item[] FindAll()
        {
            Console.WriteLine("Execute method FindAll");
            using (IDbConnection conn = Connection)
            {
                conn.Open();
                var result = conn.Query<Item>("select * from \"Goods\"");
                return result.ToArray(); //особенности сериализации IEnumerable
            }
        }

        public Item FindByID(int id)
        {
            Console.WriteLine("Execute method find");
            using (IDbConnection conn = Connection)
            {
                conn.Open();
                var res = conn.QueryFirstOrDefault<Item>("select * from \"Goods\" where \"Id\"[email protected] LIMIT 1", new { Id = id });
                return res;
            }
        }

        public void Remove(int id)
        {
            Console.WriteLine("Execute method remove");
            using (IDbConnection conn = Connection)
            {
                conn.Open();
                conn.Execute("delete from \"Goods\" where [email protected]", id);
            }
        }

        public void Update(Item item)
        {
            Console.WriteLine("Execute method Update");
            using (IDbConnection conn = Connection)
            {
                conn.Open();
                string command = "update \"Goods\" set \"Name\"[email protected],\"Description\"[email protected],\"Price\"[email protected]) ";
                command += "where \"Id\"[email protected]";
                conn.Execute(command, item);
            }
        }
То что написано в этом классе, это по сути - просто набор sql-запросов для работы с таблицей Goods из моего примера. Dapper хорош тем, что в результате sql-запроса возвращает нам не таблицу данных, а готовый объект (или коллекцию объектов ) на вызов select. И в Update тоже принимает модель.
Именно этот код будет работать только с таблицей по структуре как на скрине выше и "мапить" её в тот объект Item что мы описали.

Почитать немного про даппер можно здесь: с той лишь разницей, что у нас отличаются названия столбцов таблицы и у меня connectionString захардкожен прямо в коде класса.
Если что-то непонятно внутри самих методов - на stackoverflow полно примеров по использованию даппера. Использование даппера - не тема статьи. Этот код здесь лишь для того, чтобы показать, что WCF можно использовать с разными технологиями.

В методе Main снова поднимаем сервис:

C#:
        static void Main(string[] args)
        {
            ServiceHost host = new ServiceHost(typeof(DapperService), new Uri("http://localhost:6000/DapperService"));
            host.AddServiceEndpoint(typeof(IDapperService), new BasicHttpBinding(), "");
            host.Open();
            Console.WriteLine($"Start service {host.Description}, {host.State}");
            Console.ReadLine();
            host.Close();
            Console.WriteLine($"Close service {host.Description}, {host.State}");
            Console.ReadLine();
        }
если что-то не компилируется, не забываем про юзинги:
C#:
using Dapper;
using Npgsql;
using System;
using System.Data;
using System.Linq;
using System.ServiceModel;
using System.Xml.Serialization;
И про ссылку на System.ServiceModel
Компилируем, запускаем:
11.png


Идем в PM и начинаем говнокодить:
1. Сcылки из GAC
12.png

2. В общем коде создаем такой же класс и интерфейс как в консольке с Даппер-сервисом:
C#:
    public class Item
    {
        [XmlElement]
        public int Id { get; set; }
        [XmlElement]
        public string Name { get; set; }
        [XmlElement]
        public string Description { get; set; }
        [XmlElement]
        public decimal Price { get; set; }
    }

    [ServiceContract]
    [XmlSerializerFormat]
    public interface IDapperService
    {
        [OperationContract]
        void Add(Item item);
        [OperationContract]
        void Remove(int id);
        [OperationContract]
        void Update(Item item);
        [OperationContract]
        Item FindByID(int id);
        [OperationContract]
        Item[] FindAll();
    }
3. Реализуем подключение к службе:
C#:
    public class Helper
    {
        private static DapperService dapper;
 
        public static DapperService GetDapper()
        {
            if(dapper==null) dapper = new DapperService();
            return dapper;
        }
    }

    public class DapperService
    {
        public IDapperService service {get;set;}
        public ChannelFactory<IDapperService> factory {get;set;}
 
        public DapperService()
        {
            Uri tcpUri = new Uri("http://localhost:6000/DapperService");
            EndpointAddress addr = new EndpointAddress(tcpUri);
            BasicHttpBinding binding = new BasicHttpBinding();
            factory = new ChannelFactory<IDapperService>(binding, addr);
            service = factory.CreateChannel();
        }
    }
Немного кривовато получилось, но в примере с калькулятором, у меня была проблема с видимостью сигнатуры методов, и было очень неудобно писать код без подсказок.
Эта непонятная смесь каких-то паттернов решила проблему:
13.png

4. Добавляем кубик "свой код":
C#:
var s = Helper.GetDapper().service; //экземпляр службы

string id = Guid.NewGuid().ToString().Substring(0,8);

for (int i=1;i<15;i++)
{
    s.Add(
        new Item() {Name=id+"__"+i.ToString(),Description=DateTime.Now.TimeOfDay.ToString(),Price=(decimal)i}
    );
}
Мы просто создаем 15 записей в БД со сгенерированными названиями, в описание сохраняем время, а цену просто заполняем каким-то значением.
Запускаем в многопотоке в ZP:
Консолька соощает что вызывается метод Add,
14.png

а в БД результат:
15.png

Остальные методы тоже работают:
Helper.GetDapper().service.Remove(100); //удалит строку с Id=100
и т.д.


Таким образом, общий алгоритм работы с WCF-службами такой:
Описываем интерфейс =>
поднимаем службу =>
описываем интерфейс на клиенте =>
подключаемся к службе =>
пользуемся.



Калькулятор, Постгрес - это всё конечно интересно, но можно и без всякого WCF обойтись - быстрее и проще реализовать это прямо в PM.
Так что, поищем какой-нибудь действительно интересный и полезный нам проект на просторах интернетов.

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

И, О чудо! У Микрософта есть открытый проект на гитхабе с примерами использования нейросети ML.NET
и даже РУКОВОДСТВО как этим пользоваться!

Качаем проект из репозитория выше (для тех кто не пользовался гитом, вот тут:
есть кнопка скачать зип-архив
17.png

И открываем файл TransferLearningTF.sln (должна запуститься студия).
Компилируем, запускаем:
16.png.
(если ошибки какие-то, то возможно нужно проделать пункт 6 этапа установка:
)
если не получилось - ничего страшного, ниже будет подробней про этот этап.

Краткая суть для тех кому лень читать статью по ссылке: "предварительно обученная нейросеть на 10 картинках научилась определять по фотографиям "Еду", "Игрушку" и "Технику".
В консоли мы видим название картинки и то что "нагадала" нам эта нейросеть.

Что ж, попробуем её научить делать что-то действительно полезное, например отличать хот-дог от не хот-дога.

1. Создадим в директории /assets/ выкачанного проекта папку /hotdogs/
2. Скачаем из интернета несколько хотдогов назовем их hotdog1..12 + testhotdog и переименуем то что было в папке /images/ в nothotdog1..12 и переместим их в папку /hotdogs/
3. В этой же папке создадим два файла: tags.tsv и test-tags.tsv со следующим содержимым:
tags
C#:
hotdog1.jpg{TAB}hotdog
итд
hotdog10.jpg{TAB}hotdog
nothotdog1.jpg{TAB}nothotdog
итд до 10
test-tags
C#:
hotdog11.jpg{TAB}hotdog
hotdog12.jpg{TAB}hotdog
nothotdog11.jpg{TAB}nothotdog
nothotdog12.jpg{TAB}nothotdog

4. Повторим пункт 5 этапа "Установка" из руководства
https://docs.microsoft.com/ru-ru/dotnet/machine-learning/tutorials/image-classification#setup
для файлов во вновь созданной папке /hotdogs/.
4.1 в обозревателе VS жмем "отобразить все файлы и папки"
18.png
4.2 ПКМ по созданной папке - включить в проект.
19.png
4.3 Выделим все файлы, ПКМ-свойства
20.png
4.4 Копировать более позднюю версию
21.png
5. Меняем в 15-й строке название директории с фотками на hotdogs:
static readonly string _imagesFolder = Path.Combine(_assetsPath, "hotdogs");
а в 18й строке название файла:
static readonly string _predictSingleImage = Path.Combine(_imagesFolder, "testhotdog.jpg");
и запускаем:
Удивительно, но нейросеть вполне достойно определяет хотдоги и нехотдоги:
22.png
23.png

Поработаем немного кувалдой и напильником , чтобы сделать из этого WCF-службу, и чтобы можно было отправлять фотки из ЗенноПостера.
Главная проблема в том, что проект скомпилирован под .NetCore, а в .NetCore нет WCF и скорее всего никогда не будет, т.к. W - значит WINDOWS, а Core - это про кросс-платформенность и открытые исходники.

Так что, делаем следующее:

1. Создаем в студии новый проект под NetFramework.
2. Устанавливаем вот эти пакеты:
24.png
Делать это удобнее из диспечера пакетов, чем из командной строки:
25.png 26.png
3. Копируем папку assets/ с нашими хотдогами из старого проекта в новый
27.png
4. Снова Проделываем для нового проекта пункт 5 этапа "установка" из руководства для всех файлов из скопированной папки.
5. Полностью переносим содержимое класса Program из Core проекта в новый (с 11 по 186 строки) вставляем вместо строк (9-14 нового)
6. не забываем про юзинги:
C#:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.ML;
using Microsoft.ML.Data;
7. Запускаем: ОШИБКА. Нужно выбрать конфигурацию.
Идем в свойства проекта
28.png
И выбираем целевую платформу сборки (я выбрал x64)
29.png
Запускаем и убедимся, что всё у нас собирается.
30.png
Ничего не сломалось - уже успех.

Время напильника:
Сразу после первой открывающейся фигурной скобки файла Progran.cs начинаем кодить:
Создадим класс описывающий ответ сервера:
Класс содержит только свойства "имя" и "число"
C#:
    public class ImageTarget
    {
        [XmlElement]
        public string Name { get; set; }
        [XmlElement]
        public float Score { get; set; }
    }
И опишем интерфейс:
C#:
    [ServiceContract]
    [XmlSerializerFormat]
    public interface IFotoService
    {
        [OperationContract]
        ImageTarget CheckFoto(string path);
    }
Здесь только один метод - мы будем отправлять из ZP путь до файла с картинкой, а в ответ нейросеть будет сообщать что она там определила,
и насколько это близко к истине (чем ближе к 1 тем точнее категория того что на фото).
Всё что меньше 0,8 - почти неправда.
То что VS нам подчеркивает красным - наводим мышью и выбираем из подсказки то что там мы забыли (юзинги и ссылку на ServiceModel).

Где-то в глубине скачанного (и нового) проекта есть такой метод:
public static void ClassifySingleImage(MLContext mlContext, ITransformer model)

Именно его мы и скопипастим почти полностью для обработки фотографий при реализации нашего интерфейса. От себя добавим только статический конструктор, чтобы модель у нас обучалась лишь единожды - при запуске консольки.
Реализуем интерфейс:
C#:
    public class FotoService : IFotoService
    {
        public static MLContext _context;
        public static ITransformer _model;

        static FotoService()
        {
            _context = new MLContext();
            _model = GenerateModel(_context);
        }
        public FotoService() { if (_model == null) throw new Exception("Empty model"); }

        public ImageTarget CheckFoto(string path)
        {
//копипаста:
            // load the fully qualified image file name into ImageData
            // <SnippetLoadImageData>
            var imageData = new ImageData()
            {
                ImagePath = path
            };
            // </SnippetLoadImageData>

            // <SnippetPredictSingle>
            // Make prediction function (input = ImageData, output = ImagePrediction)
            var predictor = _context.Model.CreatePredictionEngine<ImageData, ImagePrediction>(_model);
            var prediction = predictor.Predict(imageData);
            // </SnippetPredictSingle>

            Console.WriteLine("=============== Making single image classification ===============");
            // <SnippetDisplayPrediction>
            Console.WriteLine($"Image: {Path.GetFileName(imageData.ImagePath)} predicted as: {prediction.PredictedLabelValue} with score: {prediction.Score.Max()} ");
//вместо вывода в консоль - возвращаем результат.
            return new ImageTarget() { Name = prediction.PredictedLabelValue, Score = prediction.Score.Max() };
            // </SnippetDisplayPrediction>
        }
   }
Я стараюсь максимально ничего не менять, только копипастить.

Во-первых, потому что сам не особо понимал что делаю, когда начинал работать над материалом для статьи,
а во-вторых, чтобы было понятно, что мы используем ЧЕЙ-ТО ЧУЖОЙ ПРОЕКТ ПОД VISUAL STUDIO, некоторый "черный ящик" который не понимаем как работает,
и настраиваем лишь "точку доступа" извне для использования в шаблонах ЗП.

Теперь убираем всё из метода Main, и пишем уже знакомый нам код поднятия службы:

C#:
            var service = new FotoService(); // нового здесь только эта строчка

            ServiceHost host = new ServiceHost(typeof(FotoService), new Uri("http://localhost:7001/FotoService"));
            host.AddServiceEndpoint(typeof(IFotoService), new BasicHttpBinding(), "");
            host.Open();
            Console.WriteLine($"Start service {host.Description}, {host.State}");
            Console.ReadLine();
            host.Close();
            Console.WriteLine($"Close service {host.Description}, {host.State}");
компилируем и запускаем от имени администратора .exe файл
у меня C:\Users\evgen\Documents\ZennoLab\12й конкурс\на экспорт\калькулятор\MLNET\MlNet12\bin\Debug\MlNet12.exe

Как видно по консольке:
31.png
у нас обучилась модель, протестились картинки из файла test-tags.tsv и открылась наша служба.

Теперь идем в PM и создаем простенький шаблон: (я буду использовать тот же, где мы подключались к службе dapper)

Идем в общий код. и добавляем в конце:

Описываем интерфейс:
C#:
    [ServiceContract]
    [XmlSerializerFormat]
    public interface IFotoService
    {
        [OperationContract]
        ImageTarget CheckFoto(string path);
    }
Класс описывающий ответ сервера:
C#:
    public class ImageTarget
    {
        [XmlElement]
        public string Name {get;set;}
        [XmlElement]
        public float Score {get;set;}
    }
Клиент для нашего сервиса:
C#:
    public class FotoService
    {
        public IFotoService service {get;set;}
        public ChannelFactory<IFotoService> factory {get;set;}
 
        public FotoService()
        {
            Uri tcpUri = new Uri("http://localhost:7001/FotoService");
            EndpointAddress addr = new EndpointAddress(tcpUri);
            BasicHttpBinding binding = new BasicHttpBinding();
            factory = new ChannelFactory<IFotoService>(binding, addr);
            service = factory.CreateChannel();
        }
    }
И в классе Helper вот такой код:
C#:
        private static FotoService fs;
 
        public static FotoService GetFotoService()
        {
            if(fs==null) fs = new FotoService();
            return fs;
        }
Далее создаем кубик "свой код" со следующим содержимым:
C#:
//отправляем все картинки с рабочего стола нашей нейросети
var fs = Helper.GetFotoService().service;
var list = Directory.GetFiles(@"C:\Users\evgen\Desktop","*.jpg");
foreach (var f in list)
{
    var answer = fs.CheckFoto(@f);
    var fname = Path.GetFileName(f);

    project.SendInfoToLog(
        fname+" "+
        answer.Name + ":" + answer.Score
    );
}
И запускаем.
У меня на рабочем столе винегрет из разных картинок: всякий мусор, одна голая женщина, специально подготовленные хотдоги,
и ловушки из гамбургеров и сэндвичей, проверить насколько умна нейросеть:
32.png


И вот что мы видим в логе:
33.png
и то же самое в консольке:
34.png

На что стоит обратить внимание:
Image: bugrer.jpg predicted as: hotdog with score: 0,9941276
Image: gamburger.jpg predicted as: hotdog with score: 0,530654

Наша сеть хоть и научилась на 10 картинках отличать явный треш от хотдогов, но то что внешне похоже на хотдог, она принимает за хотдог.
И хотя гамбургер со значением 0,530654 - можно смело относить к нехотдогам (все что меньше 0.8 в параметре score - это как пальцем в небо).
А вот на фотке burger.jpg со значением 0,9941276 нейросеть почти уверена, что определила хотдог.

Как мы видим, это не так. Здесь нет сосиски -
35.jpg
хотя и очень похоже.

Это была специальная ловушка для нашей нейросети, и она в неё попалась.
кроме того, нейросеть не смогла определить хотдог на желтом фоне: (не знаю почему, наверное потому что булки недостаточно контраcтируют с фоном).
Image: hotdog1.jpg predicted as: nothotdog with score: 0,6148284 Хотя по значению 0,6148284 явно что-то не то с этим результатом

Т.е. при обучении нейросети, нужно предусмотреть и такие варианты хотдогов и нехотдогов. Возможно добавить несколько сэндвичей и гамбургеров и вынести их в отдельную (третью) категорию (указать в TSV-файле имяфото.jpg{tab}имяновойкатегории).


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

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

 
Категория
Полезно

Вложения

Последнее редактирование модератором:

Sanekk

Client
Регистрация
24.06.2016
Сообщения
717
Благодарностей
261
Баллы
63
круто конечно))) но сложновато...
 
  • Спасибо
Реакции: Roman*

Manfred

Client
Регистрация
06.08.2019
Сообщения
36
Благодарностей
17
Баллы
8
Спасибо! Мощная статья, большой труд!
Теперь появилось понимание с какого бока подойти к нейронным сеткам!
 

pars

Пользователь
Регистрация
10.12.2016
Сообщения
65
Благодарностей
29
Баллы
18
Это топ!
 
  • Спасибо
Реакции: planeta

Reactor3000

Client
Регистрация
25.09.2019
Сообщения
314
Благодарностей
178
Баллы
43
Ничего не понял, но однозначно + =)
Нужно еще пару раз перечитать =)
 

Nike59

Client
Регистрация
05.08.2011
Сообщения
66
Благодарностей
47
Баллы
18
Очень познавательно. А для меня и полезно. Увидел практический пример применения нейронных сетей.
 
  • Спасибо
Реакции: AZANIR

semafor

Client
Регистрация
27.12.2016
Сообщения
125
Благодарностей
27
Баллы
28
Клиент-сервер! Вот это бомба! Да еще и с нейронкой!
 

DenisK

Client
Регистрация
28.06.2016
Сообщения
363
Благодарностей
151
Баллы
43
Интересно, но мало понятно((
 

Moadip

Client
Регистрация
26.09.2015
Сообщения
461
Благодарностей
660
Баллы
93
Интересно, но мало понятно((
Статью надо было разделить на две. WCF и нейронки под C#. Тогда бы было более понятно.:-)

По нейронкам.
Дефакто язык для ML это питон. Поэтому чтобы к примеру заюзать нейронку в C# проге, приходилось запускать ее под питоном, и потом через поднятие сервиса взаимодействовать с ней.
Т.е. проблема была в том, что нельзя просто взять готовую обученную сеть, пихнуть ее в C# прогу и запустить.
В этом плане C# повезло меньше всего из остальных языков программирования, тк норм решений не было. На том же С++ близком к C# дела обстоят лучше.

Но со временем ситуация начала улучшаться, все больше начало появляться годных решений и под C#.

Автор привел как пример нейросетевого фреймворка для C# ML.NET.
Сейчас активно развивается еще один очень интересный проект. Кто еще не видел и интересуется ML рекомендую обратить внимание - SciSharp
 

Phoenix78

Client
Регистрация
06.11.2018
Сообщения
1 563
Благодарностей
728
Баллы
113
Однозначно очень интересно, сложно и непонятно :-)
В копилочку и изучать . еще пара десятилетий и кто не освоит нейронку может оказаться за бортом . ну мое мнение имхо :-)
 

Roman*

Client
Регистрация
25.09.2013
Сообщения
1 465
Благодарностей
510
Баллы
113
Будем надеяться, что кто то вскоре упростит все это дело по нейронке до .dll или сниппета С#, а то пока очень сложно и непонятно, но очень нужно для распознавания картинок.
 

evgen2208

Client
Регистрация
12.10.2016
Сообщения
126
Благодарностей
148
Баллы
43
Дефакто язык для ML это питон. Поэтому чтобы к примеру заюзать нейронку в C# проге, приходилось запускать ее под питоном, и потом через поднятие сервиса взаимодействовать с ней.
Ну не... Сервис то тоже на шарпе запускаем.
Вообще статья задумывалась про WCF изначально, просто пока искал пример проекта который можно просто "как есть" заюзать наткнулся на ML. И он оказался интересней чем всё что можно было про WCF написать.

Кстати, а чем postgresql отличается от mysql?
На бытовом уровне почти ничем, небольшими нюансами синтаксиса только если. А в серьезных проектах вроде постгря приоритетней - нагрузку лучше держит.

Клиент-сервер! Вот это бомба! Да еще и с нейронкой!
Вообще, эта технология для распределенных приложений. Могут несколько серверов (десятков серверов) взаимодействовать в одном приложении. Но я не разбирался глубоко :-)

Нужно еще пару раз перечитать =)
Надо запустить и руками потрогать, глазами посмотреть результат, тогда понимание приходит. А перечитывать не особо работает (у меня во всяком случае)
 
  • Спасибо
Реакции: Dr.Pipetka и Advert31337

Astraport

Client
Регистрация
01.05.2015
Сообщения
3 521
Благодарностей
2 531
Баллы
113
Надо запустить и руками потрогать, глазами посмотреть результат, тогда понимание приходит. А перечитывать не особо работает (у меня во всяком случае)
Видос может лучше запилить?
 
  • Спасибо
Реакции: planeta и RoyalBank

planeta

Client
Регистрация
01.09.2015
Сообщения
81
Благодарностей
34
Баллы
18
Норм статья
 

evgen2208

Client
Регистрация
12.10.2016
Сообщения
126
Благодарностей
148
Баллы
43
Видос может лучше запилить?
На видео конкурс тогда :-) Прям по феншую - повторное использование кода.

А по существу - там же есть проекты для VS во вложениях в статье. Можно просто качать запускать.
 

Alex101

Client
Регистрация
28.06.2018
Сообщения
131
Благодарностей
22
Баллы
18
очень мощно!!! автору респект!!!
 

Zymlex

Moderator
Команда форума
Регистрация
24.10.2016
Сообщения
2 863
Благодарностей
1 461
Баллы
113
Для связи, например, между шаблонами, советую обратить внимание на более простой MagicOnion (WCF сложен и устарел).
Он работает на основе gRPC, но не требует писать protobuf файл, так как сериализатор MessagePack.
 

samsonnn

Client
Регистрация
02.06.2015
Сообщения
1 227
Благодарностей
801
Баллы
113
Да, видео не хватает. Примеры скачал, с калькулятором все работает, а вот с картинками так и не понял, как обучать, куда пихать их, или мне показалось, или не до конца все раскрыто в статье, за старание ставлю +
 
Регистрация
13.04.2016
Сообщения
464
Благодарностей
17
Баллы
18
Да, видео не хватает. Примеры скачал, с калькулятором все работает, а вот с картинками так и не понял, как обучать, куда пихать их, или мне показалось, или не до конца все раскрыто в статье, за старание ставлю +
деревня необразованная))) что тут непонятного то? всё очевидно как 2*2, на уровне школьной программы для 5 классов. А если применить вышеописанный трах-тибидох товарищем Zymlex, всё становится на свои места, смотря под каким углом смотреть))) :D
 

evgen2208

Client
Регистрация
12.10.2016
Сообщения
126
Благодарностей
148
Баллы
43

samsonnn

Client
Регистрация
02.06.2015
Сообщения
1 227
Благодарностей
801
Баллы
113
деревня необразованная))) что тут непонятного то? всё очевидно как 2*2, на уровне школьной программы для 5 классов. А если применить вышеописанный трах-тибидох товарищем Zymlex, всё становится на свои места, смотря под каким углом смотреть))) :D
образованная деревня помоги))), а то я не пойму как в 5-м классе нейронку обучить картинкам)
 

Zymlex

Moderator
Команда форума
Регистрация
24.10.2016
Сообщения
2 863
Благодарностей
1 461
Баллы
113
  • Спасибо
Реакции: Анатолий

samsonnn

Client
Регистрация
02.06.2015
Сообщения
1 227
Благодарностей
801
Баллы
113
Народ отпишитесь пожалуйста, хоть у кого то получилось разобраться как этим пользоваться? Я уже и архивы скачал, запускается консолька, как я понял это сервак, на который нужно что то отправлять, а чтоб что то отправлять, нужно что то и как то обучить чему то. Скачал с гит хаба запустилась консоль и послала меня подальше, скачал этот .NET core установил, оно меня снова посылает



сплошная мозгодробильная головоломка. ТС дай плиз свой проект который уже под .NET Framework я понимаю что где то туплю но не найду где.
 
Последнее редактирование:

Astraport

Client
Регистрация
01.05.2015
Сообщения
3 521
Благодарностей
2 531
Баллы
113

evgen2208

Client
Регистрация
12.10.2016
Сообщения
126
Благодарностей
148
Баллы
43
сплошная мозгодробильная головоломка. ТС дай плиз свой проект который уже под .NET Framework я понимаю что где то туплю но не найду где.
скоро будет. вместе с видосом
 
  • Спасибо
Реакции: Alex101 и samsonnn

Zymlex

Moderator
Команда форума
Регистрация
24.10.2016
Сообщения
2 863
Благодарностей
1 461
Баллы
113
Последнее редактирование:

Кто просматривает тему: (Всего: 1, Пользователи: 0, Гости: 1)