Чем больше я собирал данных со станка, тем важнее становился не вопрос «что ещё добавить?», а вопрос «что из этого вообще имеет смысл показывать модели?»

В одном из экспериментов у меня было уже 7230 PLC-сигналов и 666 208 временных срезов станка.

На первый взгляд — огромный датасет.

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

Если просто скормить всё это ML, модель обязательно что-нибудь найдёт.

Вопрос только — что именно она найдёт и как это потом понять?

Первый сюрприз: большая часть PLC почти не двигается

На одном из первых больших прогонов я анализировал 6299 сигналов (у нас было несколько вариантов коллектора).

Из них 5650 за исследуемый период оказались статичными.

То есть реально менялись только 649.

Это довольно хорошо отрезвляет.

Ты смотришь на карту PLC и видишь тысячи адресов:

X...
Y...
M...
D...
W...

А потом открываешь временной ряд и понимаешь, что большая часть этого богатства выглядит примерно так:

0
0
0
0
0
0
0
...

Для конкретного обучающего датасета такие признаки бесполезны.

Но здесь есть одна оговорка.

Статичный сигнал нельзя автоматически объявлять мусором на уровне самого станка.

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

Для текущего ML-эксперимента я могу его исключить.

Из каталога PLC я его исключать не могу. В этом и парадокс.

И теперь главное правило:

ML-датасет и карта сигналов станка — не одно и то же.

Датасет можно агрессивно чистить, а вот карту оборудования нельзя.

Второй сюрприз: модель очень любит подсматривать ответ (опытные ML меня поймут)

Следующая проблема оказалась интереснее.

Допустим, мы хотим предсказывать брак.

Где-то в PLC уже есть цепочка примерно такого вида:

процесс
  ↓
возникает проблема
  ↓
PLC её обнаруживает
  ↓
ставит reject / alarm / defect bit
  ↓
изделие уходит в брак

Если взять сигнал из конца этой цепочки и добавить его в признаки, ML будет счастлив.

Метрики будут великолепные.

Только никакого предсказания здесь нет.

Модель просто научилась читать ответ из PLC.

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

Потому что это никакое не ML, это просто логика работы PLC.

В одном из прогонов таких потенциально опасных признаков набралось 321.

После фильтрации у меня осталось 1322 динамических non-leakage признака.

И вот только после этого эксперимент стал интересным.

Потому что теперь модель была вынуждена искать не:

PLC уже сказал "брак"

а что-нибудь вроде:

за несколько секунд до брака
в этой части процесса
начинает меняться определённая комбинация сигналов

Вот это уже похоже на ML.

Но тут вылез ещё один подвох

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

Потому что временной ряд PLC — это далеко не всегда производство.

Там вперемешку находятся:

  • нормальная работа;
  • остановки;
  • запуск;
  • переходные состояния;
  • переналадка;
  • технические операции.

Для человека возле оборудования это очевидные вещи.

Для ML это просто разные комбинации нескольких тысяч чисел.

Я решил посмотреть на данные вообще без информации о браке и прогнал unsupervised-анализ.

PCA + KMeans довольно уверенно разделили временной ряд на несколько состояний станка.

В финальном эксперименте получилось три крупных состояния:

state 0 — 609 978 snapshots — 91.57%
state 1 — 26 342 snapshots — 3.95%
state 2 — 29 810 snapshots — 4.48%

Самый большой кластер очень походил на обычный стабильный production.

Остальные — на остановки и переходные состояния.

И тут появилась неприятная мысль.

А вдруг модель предсказывает не брак?

Представим ситуацию.

Перед браком станок часто замедляется или переходит в какое-то другое состояние.

Модель видит это и показывает хороший ROC-AUC.

Мы радуемся:

Нашли предвестник брака!

Но на самом деле она могла научиться совсем другому:

станок работает
        ↓
что-то происходит
        ↓
станок останавливается

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

Для production ML это опасная ловушка.

Особенно когда признаков тысячи.

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

Ещё одна ловушка — дубли PLC

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

А потом выяснялось, что они почти синхронны.

Например, в одном из экспериментов пары:

M5136 ↔ M5402
M5137 ↔ M5403

оказались практически дублирующими представлениями одного события в PLC.

Если не проверять такие вещи, получается забавная ситуация.

Модель говорит:

Самые важные признаки — M5136 и M5402.

Ты думаешь:

Отлично, две независимые причины подтверждают друг друга.

А физически это может оказаться одним и тем же состоянием, которое просто записано в двух разных местах программы PLC.

Поэтому корреляция между сигналами сама по себе ещё ничего не доказывает.

Нужно постоянно возвращаться от ML обратно к логике станка:

признак
  ↓
PLC-адрес
  ↓
что это за сигнал
  ↓
какое оборудование за ним стоит
  ↓
что физически происходит в этот момент

Без последнего шага получается не ML, а машинное обучение на загадочных номерах регистров. Или просто красивые цифры для красивого отчета и ничего более.

Вконце концов я перестал воспринимать PLC как обычную таблицу признаков

Это главный вывод всего этапа.

В обычном табличном ML можно представить данные примерно так:

feature_1
feature_2
feature_3
...
target

Ведь именно так мы учились на учебных ноутбуках и в кагле.

С PLC так работать нельзя.

Например у тебя есть:

D5708
D7808
M5965
X161
...

Почему бы просто не сделать из них X признаки и отправить в модель?

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

Один сигнал означает положение механизма.

Другой — расчётный параметр.

Третий — команду.

Четвёртый — подтверждение выполнения команды.

Пятый — уже готовую диагностику неисправности.

Шестой — вообще состояние интерфейса оператора.

И если смешать всё это без разбора, модель легко получает отличную метрику совершенно неправильным способом.

Как сейчас выглядит мой pipeline подготовки PLC к ML

В упрощённом виде я пришёл примерно к такой последовательности:

сырые PLC-сигналы
        ↓
проверка качества временного ряда
        ↓
удаление статичных признаков
        ↓
поиск дублей и служебных сигналов
        ↓
удаление reject-chain / leakage
        ↓
выделение режимов станка
        ↓
проверка модели внутри production
        ↓
поиск временных предвестников
        ↓
физическая расшифровка найденных сигналов

И только после этого можно серьёзно относиться к найденной закономерности.

Самое интересное, что собственно ML занимает вообще мало времени (удивительно, да?).

Большая часть работы - это работа с данными: понять что именно модель увидела.

Главный инсайт

Раньше мне казалось, что большое количество PLC-сигналов — это богатая база для ML.

Сейчас я смотрю на это по другому.

7230 сигналов — это не 7230 полезных признаков.

Это 7230 кандидатов, среди которых находятся:

  • хорошие физические параметры;
  • внутренние состояния PLC;
  • дубли;
  • константы;
  • диагностика;
  • leakage;
  • редкие события;
  • режимы оборудования;
  • и где-то среди всего этого — реальные предвестники процесса.

Работа ML-инженера как раз и заключается в том, чтобы отделить полезные сигналы от бесполезных.

Офлайн ML модель ≠ ML модель на онлайн данных

Офлайн можно сколько угодно гонять исторический датасет, менять окна и смотреть метрики.

Но для часотыт эксперимента модель ужно поставить рядом с настоящим станком и посмотреть:

что она будет говорить, когда производство идёт в реальном времени?

И об этом напишу на следующей неделе.