Полное руководство по Python для создания контролируемых систем искусственного интеллекта для семантической сегментации неструктурированных данных облака точек 3D LiDAR.

Обладание навыками и знаниями для изучения всех аспектов обработки облаков точек открывает множество идей и возможностей для развития. 🤖 Это как набор инструментов для творчества и гибкости в области 3D-исследований. И в основе лежит это невероятное пространство искусственного интеллекта, нацеленное на понимание 3D-сцены. 🏡

Это особенно актуально из-за его важности для многих приложений, таких как беспилотные автомобили, автономные роботы, 3D-картографирование, виртуальная реальность и метавселенная. И если вы такой же фанат автоматизации, как и я, трудно устоять перед искушением найти новые пути решения этих задач!

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

Но на самом деле, как мы можем применить семантическую сегментацию? И насколько сложно 3D-машинное обучение?

Позвольте мне представить четкий, углубленный практический курс 201, посвященный 3D-машинному обучению. В этом руководстве я подробно расскажу, что такое 3D-машинное обучение и как мы можем использовать эффективный код Python для создания семантических прогнозов для неструктурированных 3D-облаков точек.

Table of Contents
3D Scene Perception
✔️ 3D Sensors
✔️ 3D Scene Understanding
✔️ Classification
Semantics Addition Methods for 3D data
✔️ 3D Object Detection
✔️ 3D Semantic Segmentation
✔️ 3D Instance Segmentation
3D Predictions with Supervised learning
3D Python Workflow
✔️ Step 1: Definition and Data Curation
✔️ Step 2. Environment Set-up
✔️ Step 3. 3D Feature Engineering
✔️ Step 4. 3D Machine Learning
✔️ Step 5. Performance Analysis
Conclusion

Давайте погрузимся прямо в! 🤿

Восприятие 3D-сцены: предисловие к ИИ

Распознавание 3D-объектов в LiDAR (расшифровывается как Light Detection and Ranging) является большой проблемой из-за сложной природы захваченных 3D-данных. Необработанные облака точек, полученные с помощью методов 3D-сканирования, неструктурированы, неочищены, неупорядочены и склонны к нерегулярной выборке, что затрудняет понимание 3D-сцены. Так что нам делать? Осуществимо ли это вообще? Ха, это то, что нам нравится! Настоящий вызов!😉

👀 3D датчики

Начнем с входа в нашу систему. 3D-сенсоры (LiDAR, фотограмметрия, SAR, RADAR и камеры с определением глубины) описывают сцену через множество трехмерных точек в пространстве. Затем они могут содержать полезную информацию и позволять системам машинного обучения, которые используют эти входные данные (например, автономные автомобили и роботы), работать в реальном мире и создавать улучшенный опыт Метавселенной. Итак, у нас есть базовый ввод сенсорной информации, что дальше?

🏕️ Понимание 3D сцены

Как вы уже догадались: понимание сцены. Он описывает процесс восприятия, анализа и выработки интерпретации 3D-сцены, наблюдаемой с помощью одного или нескольких из этих датчиков (Сцена может быть даже динамической!). В дальнейшем эта процедура состоит в основном в сопоставлении информации о сигнале от датчиков, наблюдающих за Сценой, с «моделями», которые мы используем для понимания Сцены. В зависимости от того, насколько пушистое волшебство 🧙‍♂️, модели позволят уместное «понимание сцены». В низкоуровневом представлении методы извлекают и добавляют семантику из входных данных, характеризующих сцену. У этих техник есть название?

🦘/🐈‍⬛ Классификация

Ну, то, что мы классически охватываем в рамках понимания 3D-сцены, — это задача классификации. Основная цель этого шага — понять входные данные и интерпретировать различные части данных датчика. Например, у нас есть облако точек наружной сцены, такой как шоссе, собранное автономным роботом или автомобилем. Цель классификации — определить основные составляющие части сцены, чтобы узнать, какие части этого облака точек являются дорогами, какие — зданиями или где находятся люди. В этом смысле это общая категория, целью которой является извлечение определенного семантического значения из наших сенсорных данных. И оттуда мы хотим добавить семантику с разной степенью детализации. 👇

Методы добавления классической семантики для трехмерных данных

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

Позвольте мне более подробно описать каждую из этих техник.

📦 Обнаружение 3D-объектов

Первый будет охватывать методы обнаружения 3D-объектов. Это жизненно важный компонент для многих приложений. По сути, это позволяет системе фиксировать размеры, ориентацию и положение объектов в мире. В результате мы можем использовать эти 3D-обнаружения в сценариях реального мира, таких как приложения дополненной реальности, беспилотные автомобили или роботы, которые воспринимают мир с помощью ограниченных пространственных/визуальных сигналов. Симпатичные 3D-кубы, содержащие разные объекты. Но что, если мы хотим точно настроить контуры объектов?

🏘️ 3D-семантическая сегментация

Что ж, здесь мы приступим к решению проблемы с помощью методов семантической сегментации. Это одна из самых сложных задач, заключающаяся в присвоении семантических меток каждой базовой единице (т. е. каждой точке в облаке точек), которая принадлежит интересующим объектам. По сути, трехмерная семантическая сегментация направлена ​​на лучшее определение объектов, присутствующих в сцене. Трехмерное обнаружение ограничивающей рамки на стероидах, если хотите. Следовательно, это означает наличие семантической информации для каждой точки. Мы можем углубиться туда. Но все же остается ограничение: мы не можем напрямую обрабатывать разные объекты для каждой категории (класса), которые мы атакуем. Есть ли у нас и для этого методики?

🏠 Сегментация экземпляра 3D

Да! И это называется сегментацией экземпляра 3D. Он имеет еще более широкое применение: от трехмерного восприятия в автономных системах до трехмерной реконструкции в картографировании и цифровых двойниках. Например, мы могли бы представить робота-инвентаризатора, который идентифицирует стулья, может подсчитать их количество, а затем передвинуть их, схватив за четвертую ногу. Для достижения этой цели необходимо различать разные семантические метки, а также разные экземпляры с одной и той же семантической меткой. Я бы назвал сегментацию экземпляра этапом семантической сегментации на мегастероидах 😁.

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

Трехмерные прогнозы с контролируемым обучением

Если вы все еще там, значит, вы прошли мамбл-бамбл 3D-чарабию и готовы схватить миссию за ее единорог.🦄 Мы хотим извлечь семантическую информацию и внедрить ее в наши 3D-данные в виде облаков точек. Для этого мы углубим одну стратегию, которая помогает нам получать такую ​​информацию от датчика. Мы сосредоточимся на одном семействе алгоритмов, контролируемых методах обучения, в отличие от неконтролируемых методов, показанных ниже.



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



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

Но как мы можем оценить, насколько хорошо работает обученная модель? Достаточно ли визуального анализа (актуальный вопрос? 🙃)

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

И теперь теория (света) закончена! Давайте погрузимся в забавную реализацию кода Python за пять шагов🤲! Рекомендую иметь отличную 🫐 миску.

1. Определение рабочего процесса 3D-машинного обучения

Источник набора данных Aerial LiDAR Point Cloud

Вы знаете, что делать? Первый шаг, который мы делаем, — это погружение в Интернет и поиск забавных 3D-данных! На этот раз я хочу углубиться во французское (извините за то, что я такой сноб 😆) место, чтобы найти охлажденные наборы данных LiDAR: Национальный институт географии (IGN) Франции. С кампанией LiDAR HD Франция начинает сбор OpenData, где вы можете получить четкие 3D-облака точек некоторых регионов Франции! А сверху у некоторых есть ярлыки, которые позволяют не начинать с нуля, как вы найдете по ссылке ниже.



Но чтобы сделать урок простым, я зашел на указанный выше портал, выбрал данные, охватывающие часть города Луан (71), удалил информацию о географической привязке, вычислил некоторые дополнительные атрибуты (о которых я объясню в другом уроке 😜), а затем сделал его доступным в моей Open Data Drive Folder. Вас интересуют данные 3DML_urban_point_cloud.xyz и 3DML_validation.xyz. Вы можете перейти к Извлечению Flyvast WebGL, если хотите визуализировать его в Интернете.

Общая стратегия цикла

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

🤓 Примечание: Стратегия представляет собой небольшую выдержку из одного из документов, представленных на онлайн-курсах, которые я веду в 3D Geodata Academy. В этом руководстве рассматриваются шаги с 4 по 8 + 10 + 11, остальные подробно рассматриваются в курсе или в одном из руководств по этой ссылке поддержки.

2. Настройка нашего 3D-контекста Python

В этом практическом руководстве по облаку точек я сосредоточусь на эффективном и минимальном использовании библиотеки. Ради освоения python мы будем делать все это только с двумя библиотеками: Pandas и ScikitLearn. А мы будем творить чудеса 😁. Пять строк кода для запуска вашего скрипта:

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import MinMaxScaler

🤓 Примечание. Как видите, я импортирую функции и модули из библиотек по-разному. Для панд я использую import module, который требует меньше обслуживания операторов import. Однако, если вы хотите лучше контролировать доступ к элементам модуля, я рекомендую использовать from module import foo, что позволяет меньше печатать для использования foo.

Хороший! оттуда я предлагаю, чтобы мы относительно выражали наши пути, отделяя data_folder, содержащие наши наборы данных, от имени dataset, чтобы легко переключаться на лету:

data_folder=”../DATA/”
dataset="3DML_urban_point_cloud.xyz"

Теперь мы можем быстро загрузить набор данных в переменную pcd, используя Pandas. А поскольку исходный файл не является чистым и содержит NaN значений, мы будем использовать очень удобный dropna метод inplace, чтобы убедиться, что мы начинаем с отфильтрованного фрейма данных только с полными строками. Однако это означает, что по пути мы потеряем несколько точек (<1% записей), но на этот раз с этим все в порядке.

pcd=pd.read_csv(data_folder+dataset,delimiter=' ')
pcd.dropna(inplace=True)

🤓 Примечание. Аргумент inplace со значением True позволяет напрямую заменить объект python вместо создания копии фрейма данных.

3. Выбор и подготовка функций (шаг 4)

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

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

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

labels=pcd['Classification']
features=pcd[['X','Y','Z','R','G','B']]

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

Выбор функций

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

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

Для простоты мы вручную скорректируем выбор нашего текущего вектора признаков через контролируемое направление: мы проведем эксперименты и скорректируем, если результаты будут недостаточно хорошими. Готовый? 🙂

Подготовка функций

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

Давайте представим, что наш выбранный вектор признаков следующий:

features=pcd[['X','Y']]

Здесь, если бы мы использовали это для обучения нашего алгоритма, то мы застряли бы в видимом диапазоне, например, X варьируется от 0 до 100. Если после обучения в этом диапазоне модель получает будущие данные с аналогичным распределением но другой диапазон, например, X от 1100 до 1200, тогда мы могли бы получить катастрофические результаты, даже если это тот же набор данных, просто с промежуточным переводом. Действительно, для некоторых моделей значение X, превышающее 100, может привести к тому, что модель предскажет ошибочное значение, тогда как если бы мы заранее убедились, что перевели данные в тот же диапазон, что и при обучении. Прогнозы будут иметь больше шансов на то, чтобы иметь смысл.

Я обратился к концепции масштабирования и нормализации признаков. Это важная часть этапа предварительной обработки данных, но я видел, как многие новички упускают ее из виду (в ущерб своей модели машинного обучения). Ошибка, которую мы не совершим! 💪

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

from sklearn.preprocessing import MinMaxScaler
features_scaled = MinMaxScaler().fit_transform(features)

💡 Подсказка: MinMaxScaler() преобразует функции, масштабируя и переводя каждую функцию по отдельности, чтобы она находилась в заданном диапазоне, например, между нулем и единицей. Если ваши данные распределены нормально, вы можете использовать StandardScaler.

Настройка обучения 3D-машинному обучению

Хорошо, у нас есть вектор labels и правильный вектор features. Теперь нам нужно подготовить установку для этапа обучения. Для начала мы разделим оба вектора, сохраняя при этом правильное соответствие индексов между метками и функциями, чтобы использовать часть для обучения модели машинного обучения, а другую часть — только для просмотра производительности. Мы используем 60% данных для обучения и 40% для просмотра показателей, оба взяты из одного и того же распределения случайным образом. Делается с помощью функции train_test_split из scikitlearn:

X_train, X_test, y_train, y_test = train_test_split(features_scaled, labels, test_size=0.4)

🤓 Примечание. Мы используем соглашения об именах при работе с данными для задач машинного обучения. X обозначает функции (или данные), переданные в модель, а y обозначает метки. Каждый разбивается на _train или _test в зависимости от их окончательности.

Затем мы создаем объект классификатора через:

rf_classifier = RandomForestClassifier()

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

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

rf_classifier.fit(X_train, y_train)

И, наконец, вуаля! У нас есть обученная модель! Да, это так просто! Таким образом, также легко срезать путь. 😉

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

rf_predictions = rf_classifier.predict(X_test)

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

fig, axs = plt.subplots(1, 3, figsize=(20,5))
axs[0].scatter(X_test['X'], X_test['Y'], c =y_test, s=0.05)
axs[0].set_title('3D Point Cloud Ground Truth')
axs[1].scatter(X_test['X'], X_test['Y'], c = rf_predictions, s=0.05)
axs[1].set_title('3D Point Cloud Predictions')
axs[2].scatter(X_test['X'], X_test['Y'], c = y_test-rf_predictions, cmap = plt.cm.rainbow, s=0.5*(y_test-rf_predictions))
axs[2].set_title('Differences')

и если вы хотите проверить некоторые показатели, мы можем распечатать отчет о классификации с кучей чисел, используя функцию classification_report scikit-learn:

print(classification_report(y_test, rf_predictions))

Но разве мы не должны понимать, что означает каждая метрика? 🤔

4. Настройка 3D-машинного обучения (шаг 5)

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

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

  • Истинный положительный результат (TP): Наблюдение положительное и, по прогнозам, будет положительным.
  • Ложноотрицательный (FN): Наблюдение положительное, но прогнозируется отрицательное.
  • True Negative (TN): Наблюдение отрицательное и прогнозируется как отрицательное.
  • Ложноположительный результат (FP): результат наблюдения отрицательный, но прогнозируется положительный.

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

🤓Примечание. Другие глобальные метрики точности не являются подходящими мерами оценки, когда частоты классов несбалансированы, что имеет место в большинстве сценариев, как в естественных внутренних, так и в наружных сценах, поскольку доминирующие классы искажают их. . Отныне показатель F1 в наших экспериментах указывает на среднюю эффективность предложенного классификатора.

Выбор модели

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

from sklearn.neighbors import RandomForestClassifier
rf_classifier = RandomForestClassifier()
from sklearn.neighbors import KNeighborsClassifier
knn_classifier = KNeighborsClassifier()
from sklearn.neural_network import MLPClassifier
mlp_classifier = MLPClassifier(solver='lbfgs', alpha=1e-5,hidden_layer_sizes=(15, 2), random_state=1)

Затем вам просто нужно заменить XXXClassifier() в следующем блоке кода желаемым стеком алгоритмов:

XXX_classifier = XXXClassifier()
XXX_classifier.fit(X_train, y_train)
XXX_predictions = XXXclassifier.predict(X_test)
print(classification_report(y_test, XXX_predictions, target_names=['ground','vegetation','buildings']))

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

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

Train / Test Data: 60%/40% 
Number of Point in the test set: 1 351 791 / 3 379 477 pts
Features selected: ['X','Y','Z','R','G','B'] - With Normalization

Случайные леса

Начнем со случайных лесов. Какое-то заклинание магических деревьев с помощью ансамблевого алгоритма, который объединяет несколько деревьев решений, чтобы дать нам окончательный результат: общая точность 98%, основанная на поддержке 1.3 million points. Далее он раскладывается следующим образом:

╔════════════╦══════════════╦══════════╦════════════╦═════════╗
║  classes   ║    precision ║   recall ║   f1-score ║ support ║
╠════════════╬══════════════╬══════════╬════════════╬═════════╣
║ ground     ║         0.99 ║     1.00 ║       1.00 ║  690670 ║
║ vegetation ║         0.97 ║     0.98 ║       0.98 ║  428324 ║
║ buildings  ║         0.97 ║     0.94 ║       0.96 ║  232797 ║
╚════════════╩══════════════╩══════════╩════════════╩═════════╝

🤓Примечание. Здесь особо нечего сказать, но результаты впечатляющие. Наземные точки почти идеально классифицированы: отзыв 1.00 означает, что все точки, принадлежащие земле, были найдены, а точность 0.99 означает, что все еще есть крошечный предел улучшения, чтобы гарантировать отсутствие ложных срабатываний. Сразу видно, что ошибки распределены понемногу везде, что может вызвать проблемы, если их придется исправлять вручную.

K-NN

Классификатор K-ближайших соседей использует близость, чтобы делать прогнозы относительно отдельной группы точек данных. Мы получаем глобальную точность 91%, далее разложенную следующим образом:

╔════════════╦══════════════╦══════════╦════════════╦═════════╗
║  classes   ║    precision ║   recall ║   f1-score ║ support ║
╠════════════╬══════════════╬══════════╬════════════╬═════════╣
║ ground     ║         0.92 ║     0.90 ║       0.91 ║  690670 ║
║ vegetation ║         0.88 ║     0.91 ║       0.90 ║  428324 ║
║ buildings  ║         0.92 ║     0.92 ║       0.92 ║  232797 ║
╚════════════╩══════════════╩══════════╩════════════╩═════════╝

🤓 Примечание. Результаты ниже, чем у случайных лесов, что ожидаемо, поскольку мы больше подвержены локальному шуму в текущем векторном пространстве. У нас однородный баланс точности/отзыва по всем классам, что является хорошим признаком того, что мы избегаем проблем переобучения. По крайней мере, в текущей раздаче 😁.

Глубокое 3D-обучение с помощью многослойного персептрона

Многослойный персептрон (MLP) — это алгоритм нейронной сети, который изучает линейные и нелинейные отношения данных. MLP требует настройки нескольких гиперпараметров, таких как количество скрытых нейронов, слоев и итераций, что затрудняет получение высокой производительности из коробки. Например, с установленными гиперпараметрами мы имеем глобальную точность 64%, далее разложенную следующим образом:

╔════════════╦══════════════╦══════════╦════════════╦═════════╗
║  classes   ║    precision ║   recall ║   f1-score ║ support ║
╠════════════╬══════════════╬══════════╬════════════╬═════════╣
║ ground     ║         0.63 ║     0.76 ║       0.69 ║  690670 ║
║ vegetation ║         0.69 ║     0.74 ║       0.71 ║  428324 ║
║ buildings  ║         0.50 ║     0.13 ║       0.20 ║  232797 ║
╚════════════╩══════════════╩══════════╩════════════╩═════════╝

🤓 Примечание. Метрики MLP намеренно служат хорошим примером того, что считается плохими метриками. У нас показатель точности ниже 75%, что часто является первоначальным показателем, на который нужно ориентироваться, а затем мы видим значительные различия между классами Intra и Inter. Примечательно, что класс зданий очень далек от надежности, и у нас может возникнуть проблема переобучения. Визуально это также видно, так как мы видим, что это основной источник путаницы в модели глубокого обучения.

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

5. Производительность 3D-машинного обучения: на пути к обобщению

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

Набор данных проверки

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

  • Данные для обучения: выборка данных, используемых для подбора модели.
  • Тестовые данные: выборка данных, используемая для объективной оценки модели, подогнанной к обучающим данным, но используемая для настройки гиперпараметров модели и вектора признаков. Таким образом, оценка становится немного предвзятой, поскольку мы использовали ее для настройки входных параметров.
  • Проверочные данные. Некоррелированная выборка данных используется для объективной оценки окончательной модели, подходящей для обучающих данных.

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

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

Выбранные проверочные данные взяты из города Маноск (04), который представляет другой городской контекст, с другой топографией и совершенно другим городским контекстом, например, как показано ниже. Таким образом, мы усложняем задачу справиться с Обобщением 😆.

Вы можете загрузить набор данных 3DML_validation.xyz из моей Открытой папки на диске данных, если это еще не сделано. Как объяснено ниже, вы также найдете метки для изучения метрик и потенциальных выгод от различных итераций, которые я сделал.

Улучшение результатов обобщения

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

Во-первых, мы импортируем данные проверки в наш скрипт со следующими тремя строками кода:

val_dataset="3DML_validation.xyz"
val_pcd=pd.read_csv(data_folder+dataset,delimiter=' ')
val_pcd.dropna(inplace=True)

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

val_labels=val_pcd['Classification']
val_features=val_pcd[['X','Y','Z','R','G','B']]
val_features_scaled = MinMaxScaler().fit_transform(val_features)

Затем мы применяем уже обученную модель к проверочным данным и печатаем результаты:

val_predictions = rf_classifier.predict(val_features_scaled)
print(classification_report(val_labels, val_predictions, target_names=['ground','vegetation','buildings']))

Это оставляет нам окончательную точность 54% для 3.1 million точек (против 98% для тестовых данных, содержащих 1.3 million точек), присутствующих в наборе данных проверки. Он раскладывается следующим образом:

╔════════════╦══════════════╦══════════╦════════════╦═════════╗
║  classes   ║    precision ║   recall ║   f1-score ║ support ║
╠════════════╬══════════════╬══════════╬════════════╬═════════╣
║ ground     ║         0.65 ║     0.16 ║       0.25 ║ 1188768 ║
║ vegetation ║         0.59 ║     0.85 ║       0.70 ║ 1315231 ║
║ buildings  ║         0.43 ║     0.67 ║       0.53 ║  613317 ║
╚════════════╩══════════════╩══════════╩════════════╩═════════╝

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

features=pcd[['Z','R','G','B','omnivariance_2','normal_cr_2','NumberOfReturns','planarity_2','omnivariance_1','verticality_1']]
val_features=val_pcd[['Z','R','G','B','omnivariance_2','normal_cr_2','NumberOfReturns','planarity_2','omnivariance_1','verticality_1']]

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

features_scaled = MinMaxScaler().fit_transform(features)
X_train, X_test, y_train, y_test = train_test_split(features_scaled, labels, test_size=0.3)
rf_classifier = RandomForestClassifier(n_estimators = 10)
rf_classifier.fit(X_train, y_train)
rf_predictions = rf_classifier.predict(X_test)
print(classification_report(y_test, rf_predictions, target_names=['ground','vegetation','buildings']))
val_features_scaled = MinMaxScaler().fit_transform(val_features)
val_rf_predictions = rf_classifier.predict(val_features_scaled)
print(classification_report(val_labels, val_rf_predictions, target_names=['ground','vegetation','buildings']))

Изучим результаты. Теперь у нас есть точность 97% на тестовых данных, далее разложенных следующим образом:

╔════════════╦══════════════╦══════════╦════════════╦═════════╗
║  classes   ║    precision ║   recall ║   f1-score ║ support ║
╠════════════╬══════════════╬══════════╬════════════╬═════════╣
║ ground     ║         0.97 ║     0.98 ║       0.98 ║  518973 ║
║ vegetation ║         0.97 ║     0.98 ║       0.97 ║  319808 ║
║ buildings  ║         0.95 ║     0.91 ║       0.93 ║  175063 ║
╚════════════╩══════════════╩══════════╩════════════╩═════════╝

Добавление функций привело к небольшому падению производительности по сравнению с использованием только базового набора X, Y, Z, R, G, B, что показывает, что мы добавили немного шума. Но это того стоило ради Обобщения! Теперь у нас есть глобальная точность 85 % на проверочном наборе, то есть увеличение на 31 % только за счет выбора функций! Это массивно. И, как вы можете заметить, здания являются центральным аспектом, который портит представление. Это объясняется главным образом тем, что они сильно отличаются от тестового набора и что набор признаков не может по-настоящему представить их в некоррелированном контексте.

╔════════════╦══════════════╦══════════╦════════════╦═════════╗
║  classes   ║    precision ║   recall ║   f1-score ║ support ║
╠════════════╬══════════════╬══════════╬════════════╬═════════╣
║ ground     ║         0.89 ║     0.81 ║       0.85 ║ 1188768 ║
║ vegetation ║         0.92 ║     0.92 ║       0.92 ║ 1315231 ║
║ buildings  ║         0.68 ║     0.80 ║       0.73 ║  613317 ║
╚════════════╩══════════════╩══════════╩════════════╩═════════╝

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

Предположим, мы хотели бы масштабироваться еще больше. В этом случае может быть интересно ввести некоторые данные из распределения валидации, чтобы проверить, нужны ли они в модели, ценой того, что наша валидация потеряет свой статус и станет частью тестового набора. Мы берем 10% проверочного набора данных и 60% исходного набора данных для обучения модели случайного леса. Затем мы используем его и проверяем результаты на оставшихся 40%, составляющих тестовые данные, и на 90% проверочных данных:

val_labels=val_pcd['Classification']
val_features=val_pcd[['Z','R','G','B','omnivariance_2','normal_cr_2','NumberOfReturns','planarity_2','omnivariance_1','verticality_1']]
val_features_sampled, val_features_test, val_labels_sampled, val_labels_test = train_test_split(val_features, val_labels, test_size=0.9)
val_features_scaled_sample = MinMaxScaler().fit_transform(val_features_test)
labels=pd.concat([pcd['Classification'],val_labels_sampled])
features=pd.concat([pcd[['Z','R','G','B','omnivariance_2','normal_cr_2','NumberOfReturns','planarity_2','omnivariance_1','verticality_1']],val_features_sampled])
features_scaled = MinMaxScaler().fit_transform(features)
X_train, X_test, y_train, y_test = train_test_split(features_scaled, labels, test_size=0.4)
rf_classifier = RandomForestClassifier(n_estimators = 10)
rf_classifier.fit(X_train, y_train)
rf_predictions = rf_classifier.predict(X_test)
print(classification_report(y_test, rf_predictions, target_names=['ground','vegetation','buildings']))
val_rf_predictions_90 = rf_classifier.predict(val_features_scaled_sample)
print(classification_report(val_labels_test, val_rf_predictions_90, target_names=['ground','vegetation','buildings']))

И, к нашему большому удовольствию, мы видим, что наши показатели увеличились как минимум на 5%, при этом потеряв всего 1% на тестовом наборе, таким образом, за счет минимального шума функций, как показано ниже:

40% Test Predicitions - Accuracy = 0.96    1476484

╔════════════╦══════════════╦══════════╦════════════╦═════════╗
║  classes   ║    precision ║   recall ║   f1-score ║ support ║
╠════════════╬══════════════╬══════════╬════════════╬═════════╣
║ ground     ║         0.97 ║     0.98 ║       0.97 ║  737270 ║
║ vegetation ║         0.97 ║     0.97 ║       0.97 ║  481408 ║
║ buildings  ║         0.94 ║     0.90 ║       0.95 ║  257806 ║
╚════════════╩══════════════╩══════════╩════════════╩═════════╝
90% Validation Predicitions - Accuracy = 0.90    2805585
╔════════════╦══════════════╦══════════╦════════════╦═════════╗
║  classes   ║    precision ║   recall ║   f1-score ║ support ║
╠════════════╬══════════════╬══════════╬════════════╬═════════╣
║ ground     ║         0.88 ║     0.92 ║       0.90 ║  237194 ║
║ vegetation ║         0.93 ║     0.94 ║       0.94 ║  263364 ║
║ buildings  ║         0.87 ║     0.79 ║       0.83 ║  122906 ║
╚════════════╩══════════════╩══════════╩════════════╩═════════╝

Что обычно интересно, так это проверить окончательные результаты с разными моделями и теми же параметрами, а затем пройти заключительную фазу настройки гиперпараметров. Но это в другой раз 😉. Вы не истощены? Я думаю, что энергия нашего мозга нуждается в подзарядке; оставим остальное на другой раз и доработаем проект. 😁

Экспорт помеченного набора данных

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

val_pcd['predictions']=val_rf_predictions
result_folder="../DATA/RESULTS/"
val_pcd[['X','Y','Z','R','G','B','predictions']].to_csv(result_folder+dataset.split(".")[0]+"_result_final.xyz", index=None, sep=';')

Экспорт 3D-модели машинного обучения

И, конечно же, если вы удовлетворены своей моделью, вы можете сохранить ее навсегда, а затем поместить куда-нибудь для использования в производстве для невидимых/немаркированных наборов данных. Для этого мы можем использовать модуль pickle. Три маленькие строки кода:

import pickle
pickle.dump(rf_classifier, open(result_folder+"urban_classifier.poux", 'wb'))

И когда вам нужно повторно использовать модель:

model_name="urban_classifier.poux"
loaded_model = pickle.load(open(result_folder+model_name, 'rb'))
predictions = loaded_model.predict(data_to_predict)
print(classification_report(y_test, loaded_predictions, target_names=['ground','vegetation','buildings']))

Вы можете получить доступ к полному коду прямо в своем браузере с помощью этой записной книжки Google Colab.

Заключение

Это было сумасшедшее путешествие! Полный курс 201 с практическим руководством по 3D-машинному обучению! 😁 Вы многому научились, особенно как импортировать облака точек с функциями, выбирать, обучать и настраивать контролируемую 3D-модель машинного обучения, а также экспортировать ее для обнаружения занятий на открытом воздухе с отличным обобщением для больших наборов данных облака точек с воздуха! Массовые поздравления! Но это только часть уравнения для машинного обучения 3D. Чтобы расширить результаты обучения, в будущих статьях будут подробно рассмотрены семантическая сегментация и сегментация экземпляров [2–4], анимация и глубокое обучение [1]. Мы рассмотрим управление данными больших облаков точек, как это определено в статье ниже.



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

Рекомендации

1. Poux, F., & J.-J Ponciano. (2020). Самообучающаяся онтология, например, для сегментации трехмерного облака точек в помещении. ISPRS, международный Арка Фо. & Рем. XLIII-B2, 309–316; https://doi.org/10.5194/isprs-archives-XLIII-B2–2020–309–2020

2. Пу Ф. и Биллен Р. (2019). Семантическая сегментация 3D-облака точек на основе вокселей: неконтролируемая геометрия и взаимосвязь с методами глубокого обучения. Международный геоинформационный журнал ISPRS. 8(5), 213; https://doi.org/10.3390/ijgi8050213

3. Пу Ф., Невиль Р., Нис Г.-А. и Биллен Р. (2018). Семантическое моделирование 3D-облака точек: интегрированная структура для внутренних пространств и мебели. Remote Sensing, 10(9), 1412. https://doi.org/10.3390/rs10091412

4. Пу Ф., Невиль Р., Ван Верш Л., Нис Г.-А. и Биллен Р. (2017). Трехмерные облака точек в археологии: достижения в сборе, обработке и интеграции знаний, применяемые к квазиплоским объектам. Науки о Земле, 7(4), 96. https://doi.org/10.3390/GEOSCIENCES7040096