exponenta event banner

Классификация облаков точек с использованием глубинного обучения

В этом примере показано, как обучить сеть, предназначенную для классификации облака точек.

Данные облака точек собираются различными датчиками, такими как лидар, радар и камеры глубины. Эти датчики фиксируют 3-D информацию о положении объектов в сцене, что полезно для многих приложений в автономном вождении и дополненной реальности. Например, различение транспортных средств от пешеходов имеет решающее значение для планирования пути автономного транспортного средства. Однако обучение надежных классификаторов данным облака точек является сложной задачей из-за разреженности данных на объект, окклюзии объектов и шума датчиков. Было показано, что методы глубокого обучения решают многие из этих проблем, изучая надежные представления функций непосредственно из данных облака точек. Один из оригинальных глубоких методов изучения для классификации облаков пункта - PointNet [1].

В этом примере выполняется подготовка классификатора по набору данных Sydney Urban Objects, созданному Сиднейским университетом [2]. Этот набор данных обеспечивает сбор данных облака точек, полученных из городской среды с помощью лидарного датчика. Набор данных содержит 100 помеченных объектов из 14 различных категорий, таких как автомобиль, пешеход и автобус.

Загрузить набор данных

Загрузите и извлеките набор данных Sydney Urban Objects во временный каталог.

downloadDirectory = tempdir;
datapath = downloadSydneyUrbanObjects(downloadDirectory);

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

foldsTrain = 1:3;
foldsVal = 4;
dsTrain = loadSydneyUrbanObjectsData(datapath,foldsTrain);
dsVal = loadSydneyUrbanObjectsData(datapath,foldsVal);

Прочитайте один из учебных образцов и визуализируйте его с помощью pcshow.

data = read(dsTrain);
ptCloud = data{1,1};
label = data{1,2};

figure
pcshow(ptCloud.Location,[0 1 0],"MarkerSize",40,"VerticalAxisDir","down")
xlabel("X")
ylabel("Y")
zlabel("Z")
title(label)

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

dsLabelCounts = transform(dsTrain,@(data){data{2} data{1}.Count});
labelCounts = readall(dsLabelCounts);
labels = vertcat(labelCounts{:,1});
counts = vertcat(labelCounts{:,2});

Затем используйте гистограмму для визуализации распределения классов.

figure
histogram(labels)

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

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

rng(0)
[G,classes] = findgroups(labels);
numObservations = splitapply(@numel,labels,G);
desiredNumObservationsPerClass = max(numObservations);
files = splitapply(@(x){randReplicateFiles(x,desiredNumObservationsPerClass)},dsTrain.Files,G);
files = vertcat(files{:});
dsTrain.Files = files;

Увеличение объема данных

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

dsTrain = transform(dsTrain,@augmentPointCloud);

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

data = preview(dsTrain);
ptCloud = data{1,1};
label = data{1,2};

figure
pcshow(ptCloud.Location,[0 1 0],"MarkerSize",40,"VerticalAxisDir","down")
xlabel("X")
ylabel("Y")
zlabel("Z")
title(label)

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

Предварительная обработка данных

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

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

minPointCount = splitapply(@min,counts,G);
maxPointCount = splitapply(@max,counts,G);
meanPointCount = splitapply(@(x)round(mean(x)),counts,G);

stats = table(classes,numObservations,minPointCount,maxPointCount,meanPointCount)
stats=14×5 table
       classes        numObservations    minPointCount    maxPointCount    meanPointCount
    ______________    _______________    _____________    _____________    ______________

    4wd                      15               140              1955              751     
    building                 15               193              8455             2708     
    bus                      11               126             11767             2190     
    car                      64                52              2377              528     
    pedestrian              107                20               297              110     
    pillar                   15                80               751              357     
    pole                     15                13               253               90     
    traffic lights           36                38               352              161     
    traffic sign             40                18               736              126     
    tree                     24                53              2953              470     
    truck                     9               445              3013             1376     
    trunk                    42                32               766              241     
    ute                      12                90              1380              580     
    van                      28                91              5809             1125     

Из-за большой величины внутриклассовой и межклассовой вариабельности в количестве баллов на класс, выбор значения, которое подходит для всех классов, затруднён. Одна эвристика состоит в том, чтобы выбрать достаточное количество точек для адекватного захвата формы объектов, не увеличивая при этом вычислительные затраты за счет обработки слишком большого количества точек. Значение 1024 обеспечивает хороший компромисс между этими двумя гранями. Можно также выбрать оптимальное количество точек на основе эмпирического анализа. Однако это выходит за рамки данного примера. Используйте transform для выбора 1024 точек в наборах обучения и проверки.

numPoints = 1024;
dsTrain = transform(dsTrain,@(data)selectPoints(data,numPoints));
dsVal = transform(dsVal,@(data)selectPoints(data,numPoints));

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

dsTrain = transform(dsTrain,@preprocessPointCloud);
dsVal = transform(dsVal,@preprocessPointCloud);

Предварительный просмотр дополненных и предварительно обработанных учебных данных.

data = preview(dsTrain);
figure
pcshow(data{1,1},[0 1 0],"MarkerSize",40,"VerticalAxisDir","down");
xlabel("X")
ylabel("Y")
zlabel("Z")
title(data{1,2})

Определите модель PointNet

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

Кроме того, модель Encoder (Кодировщик В Сети) состоит из четырех моделей, за которыми следует макс. операция.

  1. Модель входного преобразования

  2. Общая модель MLP

  3. Модель преобразования элементов

  4. Общая модель MLP

Совместно используемая модель MLP реализуется с использованием ряда операций свертки, пакетной нормализации и ReLU. Операция свертки сконфигурирована таким образом, что веса совместно используются в облаке входных точек. Модель преобразования состоит из общей MLP и обучаемой матрицы преобразования, которая применяется к каждому облаку точек. Совместно используемый MLP и макс. операция делают encoder (Кодер В Сети) инвариантным к порядку, в котором обрабатываются точки, в то время как модель преобразования обеспечивает инвариантность к изменениям ориентации.

Определение параметров модели Net Encoder

Общие модели MLP и преобразования параметризуются количеством входных каналов и размерами скрытых каналов. Значения, выбранные в этом примере, выбираются путем настройки этих гиперпараметров в наборе данных Sydney Urban Objects. Обратите внимание на то, что если Вы хотите применить StartNet к другому набору данных, необходимо выполнить дополнительную гиперпараметрическую настройку.

Установите размер входного канала модели входного преобразования равным трем, а размер скрытого канала - 64, 128 и 256 и используйте initializeTransform вспомогательная функция, перечисленная в конце этого примера, для инициализации параметров модели.

inputChannelSize = 3;
hiddenChannelSize1 = [64,128];
hiddenChannelSize2 = 256;
[parameters.InputTransform, state.InputTransform] = initializeTransform(inputChannelSize,hiddenChannelSize1,hiddenChannelSize2);

Установите размер первого общего входного канала модели MLP равным трем, а размер скрытого канала равным 64 и используйте initializeSharedMLP вспомогательная функция, перечисленная в конце этого примера, для инициализации параметров модели.

inputChannelSize = 3;
hiddenChannelSize = [64 64];
[parameters.SharedMLP1,state.SharedMLP1] = initializeSharedMLP(inputChannelSize,hiddenChannelSize);

Задайте для модели преобразования элементов размер входного канала 64, а для скрытого канала 64, 128 и 256 и используйте initializeTransform вспомогательная функция, перечисленная в конце этого примера, для инициализации параметров модели.

inputChannelSize = 64;
hiddenChannelSize1 = [64,128];
hiddenChannelSize2 = 256;
[parameters.FeatureTransform, state.FeatureTransform] = initializeTransform(inputChannelSize,hiddenChannelSize,hiddenChannelSize2);

Установите размер входного канала второй совместно используемой модели MLP 64, а размер скрытого канала 64 и используйте initializeSharedMLP функция, перечисленная в конце этого примера, для инициализации параметров модели.

inputChannelSize = 64;
hiddenChannelSize = 64;
[parameters.SharedMLP2,state.SharedMLP2] = initializeSharedMLP(inputChannelSize,hiddenChannelSize);

Определение параметров модели классификатора «» GroupNet

Модель классификатора PointNet состоит из общего MLP, полностью связанной операции и softmax активации. Установите входной размер модели классификатора 64, а размер скрытого канала 512 и 256 и используйте initalizeClassifier вспомогательная функция, перечисленная в конце этого примера, для инициализации параметров модели.

inputChannelSize = 64;
hiddenChannelSize = [512,256];
numClasses = numel(classes);
[parameters.ClassificationMLP, state.ClassificationMLP] = initializeClassificationMLP(inputChannelSize,hiddenChannelSize,numClasses);

Определение функции StartNet

Создание функции pointnetClassifier, перечисленных в разделе «Функция модели» в конце примера, чтобы вычислить выходные данные модели, основанной на StartNet. Функциональная модель принимает в качестве входных данных данные облака точек, обучаемые параметры модели, состояние модели и флаг, который определяет, возвращает ли модель выходные данные для обучения или прогнозирования. Сеть возвращает прогнозы для классификации облака входных точек.

Определение функции градиентов модели

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

Укажите параметры обучения

Подготовка на 10 периодов и загрузка данных партиями по 128. Установите начальную скорость обучения 0.002, а коэффициент регуляции L2 0.01.

numEpochs = 10;
learnRate = 0.002;
miniBatchSize = 128;
l2Regularization = 0.01;
learnRateDropPeriod = 15;
learnRateDropFactor = 0.5;

Инициализируйте параметры оптимизации Adam.

gradientDecayFactor = 0.9;
squaredGradientDecayFactor = 0.999;

Железнодорожная сеть

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

Перетасовать данные в начале тренировки.

Для каждой итерации:

  • Прочитайте пакет данных.

  • Оцените градиенты модели.

  • Примените регуляризацию веса L2.

  • Использовать adamupdate для обновления параметров модели.

  • Обновите график хода обучения.

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

После завершения learnRateDropPeriod эпохи, снизить уровень обучения в раз learnRateDropFactor.

Инициализируйте скользящее среднее градиентов параметров и квадратов по элементам градиентов, используемых оптимизатором Адама.

avgGradients = [];
avgSquaredGradients = [];

Обучить модель, если doTraining является правдой. В противном случае загрузите предварительно подготовленную сеть.

Обратите внимание, что обучение было проверено на NVIDIA Titan X с 12 ГБ памяти GPU. Если в графическом процессоре меньше памяти, во время обучения может не хватить памяти. Если это произойдет, опустите miniBatchSize. Обучение этой сети занимает около 5 минут. В зависимости от оборудования графического процессора это может занять больше времени.

doTraining = false;

if doTraining
    
    % Create a minibatchqueue to batch data from training and validation
    % datastores. Use the batchData function, listed at the end of the
    % example, to batch the point cloud data and one-hot encode the label 
    % data.
    numOutputsFromDSRead = 2;
    mbqTrain = minibatchqueue(dsTrain,numOutputsFromDSRead,...
        "MiniBatchSize", miniBatchSize,...
        "MiniBatchFcn",@batchData,...
        "MiniBatchFormat",["SCSB" "BC"]);
    
    mbqVal = minibatchqueue(dsVal,numOutputsFromDSRead,...
        "MiniBatchSize", miniBatchSize,... 
        "MiniBatchFcn",@batchData,...
        "MiniBatchFormat",["SCSB" "BC"]);
 
    % Use the configureTrainingProgressPlot function, listed at the end of the
    % example, to initialize the training progress plot to display the training
    % loss, training accuracy, and validation accuracy.
    [lossPlotter, trainAccPlotter,valAccPlotter] = initializeTrainingProgressPlot;
    
    numClasses = numel(classes);
    iteration = 0;
    start = tic;
    for epoch = 1:numEpochs
        
        % Shuffle data every epoch.
        shuffle(mbqTrain);
      
        % Iterate through data set.
        while hasdata(mbqTrain)
            iteration = iteration + 1;
            
            % Read next batch of training data.
            [XTrain, YTrain] = next(mbqTrain);            
            
            % Evaluate the model gradients and loss using dlfeval and the
            % modelGradients function.
            [gradients, loss, state, acc] = dlfeval(@modelGradients,XTrain,YTrain,parameters,state);
            
            % L2 regularization.
            gradients = dlupdate(@(g,p) g + l2Regularization*p,gradients,parameters);
            
            % Update the network parameters using the Adam optimizer.
            [parameters, avgGradients, avgSquaredGradients] = adamupdate(parameters, gradients, ...
                avgGradients, avgSquaredGradients, iteration,...
                learnRate,gradientDecayFactor, squaredGradientDecayFactor);
            
            % Update the training progress.
            D = duration(0,0,toc(start),"Format","hh:mm:ss");
            title(lossPlotter.Parent,"Epoch: " + epoch + ", Elapsed: " + string(D))
            addpoints(lossPlotter,iteration,double(gather(extractdata(loss))))
            addpoints(trainAccPlotter,iteration,acc);
            drawnow
        end
        
        % Evaluate the model on validation data.
        cmat = sparse(numClasses,numClasses);
        while hasdata(mbqVal)
            
            % Read next batch of validation data.
            [XVal, YVal] = next(mbqVal);

            % Compute label predictions.
            isTraining = false;
            YPred = pointnetClassifier(XVal,parameters,state,isTraining);
            
            % Choose prediction with highest score as the class label for
            % XTest.
            [~,YValLabel] = max(YVal,[],1);
            [~,YPredLabel] = max(YPred,[],1);
            
            % Collect confusion metrics.
            cmat = aggreateConfusionMetric(cmat,YValLabel,YPredLabel);
        end
        
        % Update training progress plot with average classification accuracy.
        acc = sum(diag(cmat))./sum(cmat,"all");
        addpoints(valAccPlotter,iteration,acc);
        
        % Upate the learning rate.
        if mod(epoch,learnRateDropPeriod) == 0
            learnRate = learnRate * learnRateDropFactor;
        end
        
        % Reset training and validation data queues.
        reset(mbqTrain);
        reset(mbqVal);
    end

else
    % Download pretrained model parameters, model state, and validation
    % results.
    pretrainedURL = 'https://ssd.mathworks.com/supportfiles/vision/data/pointnetSydneyUrbanObjects.zip'; 
    pretrainedResults = downloadPretrainedPointNet(pretrainedURL);
   
    parameters = pretrainedResults.parameters;
    state = pretrainedResults.state;
    cmat = pretrainedResults.cmat;
    
    % Move model parameters to the GPU if possible and convert to a dlarray.
    parameters = prepareForPrediction(parameters,@(x)dlarray(toDevice(x,canUseGPU)));
    
    % Move model state to the GPU if possible.
    state = prepareForPrediction(state,@(x)toDevice(x,canUseGPU));
end

Отображение матрицы путаницы проверки.

figure
chart = confusionchart(cmat,classes);

Вычислите среднюю точность обучения и проверки.

acc = sum(diag(cmat))./sum(cmat,"all")
acc = 0.5742

Из-за ограниченного количества обучающих образцов в наборе данных Sydney Urban Objects повышение точности проверки более 60% является сложной задачей. Модель легко переопределяет данные обучения в отсутствие увеличения, определенного в augmentPointCloudData функция помощника. Для повышения надежности классификатора, необходимо дополнительное обучение.

Классифицировать данные облака точек с помощью StartNet

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

ptCloud = pcread("car.pcd");
X = preprocessPointCloud(ptCloud);
dlX = dlarray(X{1},"SCSB");

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

YPred = pointnetClassifier(dlX,parameters,state,false);
[~,classIdx] = max(YPred,[],1);

Отображение облака точек и прогнозируемой метки с наивысшим баллом.

figure
pcshow(ptCloud.Location,[0 1 0],"MarkerSize",40,"VerticalAxisDir","down")
title(classes(classIdx))

Функция градиентов модели

Функция ModelGradients принимает в качестве входных данных мини-пакет данных dlX, соответствующая цель dlYи обучаемые параметры, и возвращает градиенты потерь относительно обучаемых параметров и соответствующих потерь. Потеря включает в себя член регуляризации, предназначенный для того, чтобы гарантировать, что матрица преобразования признаков, предсказанная шифратором, является приблизительно ортогональной. Чтобы вычислить градиенты, вычислите modelGradients с помощью функции dlfeval функция в обучающем цикле.

function [gradients, loss, state, acc] = modelGradients(X,Y,parameters,state)

% Execute the model function.
isTraining = true;
[YPred,state,dlT] = pointnetClassifier(X,parameters,state,isTraining);

% Add regularization term to ensure feature transform matrix is
% approximately orthogonal.
K = size(dlT,1);
B = size(dlT, 4);
I = repelem(eye(K),1,1,1,B);
dlI = dlarray(I,"SSCB");
treg = mse(dlI,pagemtimes(dlT,permute(dlT,[2 1 3 4])));
factor = 0.001;

% Compute the loss.
loss = crossentropy(YPred,Y) + factor*treg;

% Compute the parameter gradients with respect to the loss. 
gradients = dlgradient(loss, parameters);

% Compute training accuracy metric.
[~,YTest] = max(Y,[],1);
[~,YPred] = max(YPred,[],1);
acc = gather(extractdata(sum(YTest == YPred)./numel(YTest)));

end

Функция-классификатор «» StartNet

pointnetClassifier функция принимает в качестве входных данных данные облака точек dlX, параметры обучаемой модели, состояние модели и флаг isTraining, который определяет, возвращает ли модель выходные данные для обучения или прогнозирования. Затем функция вызывает кодировщик, а также многослойный перцептрон для извлечения классификационных признаков. Во время обучения отсев применяется после каждой операции перцептрона. После последнего перцептрона, fullyconnect операция сопоставляет функции классификации с количеством классов, и активация softmax используется для нормализации выходного сигнала в распределение вероятностей меток. Распределение вероятностей, обновленное состояние модели и матрица преобразования признаков, предсказанная с помощью кодера, возвращают в качестве выходных данных.

function [dlY,state,dlT] = pointnetClassifier(dlX,parameters,state,isTraining)

% Invoke the PointNet encoder.
[dlY,state,dlT] = pointnetEncoder(dlX,parameters,state,isTraining);

% Invoke the classifier.
p = parameters.ClassificationMLP.Perceptron;
s = state.ClassificationMLP.Perceptron;
for k = 1:numel(p) 
     
    [dlY, s(k)] = perceptron(dlY,p(k),s(k),isTraining);
      
    % If training, apply inverted dropout with a probability of 0.3.
    if isTraining
        probability = 0.3; 
        dropoutScaleFactor = 1 - probability;
        dropoutMask = ( rand(size(dlY), "like", dlY) > probability ) / dropoutScaleFactor;
        dlY = dlY.*dropoutMask;
    end
    
end
state.ClassificationMLP.Perceptron = s;

% Apply final fully connected and softmax operations.
weights = parameters.ClassificationMLP.FC.Weights;
bias = parameters.ClassificationMLP.FC.Bias;
dlY = fullyconnect(dlY,weights,bias);
dlY = softmax(dlY);
end

Функция Encoder (Encoder-функция (Encoder-функция

pointnetEncoder функция обрабатывает входной dlX с использованием входного преобразования, совместно используемого MLP, преобразования признаков, второго совместно используемого MLP и операции max, и возвращает результат операции max.

function [dlY,state,T] = pointnetEncoder(dlX,parameters,state,isTraining)
% Input transform.
[dlY,state.InputTransform] = dataTransform(dlX,parameters.InputTransform,state.InputTransform,isTraining);

% Shared MLP.
[dlY,state.SharedMLP1.Perceptron] = sharedMLP(dlY,parameters.SharedMLP1.Perceptron,state.SharedMLP1.Perceptron,isTraining);

% Feature transform.
[dlY,state.FeatureTransform,T] = dataTransform(dlY,parameters.FeatureTransform,state.FeatureTransform,isTraining);

% Shared MLP.
[dlY,state.SharedMLP2.Perceptron] = sharedMLP(dlY,parameters.SharedMLP2.Perceptron,state.SharedMLP2.Perceptron,isTraining);

% Max operation.
dlY = max(dlY,[],1);
end

Общая многослойная функция перцептрона

Общий многослойный perceptron функция обрабатывает входной dlX с помощью ряда операций перцептрона и возвращает результат последнего перцептрона.

function [dlY,state] = sharedMLP(dlX,parameters,state,isTraining)
dlY = dlX;
for k = 1:numel(parameters) 
    [dlY, state(k)] = perceptron(dlY,parameters(k),state(k),isTraining);
end
end

Функция перцептрона

Функция перцептрона обрабатывает входной dlX с помощью свертки, пакетной нормализации и операции relu и возвращает выходные данные операции ReLU.

function [dlY,state] = perceptron(dlX,parameters,state,isTraining)
% Convolution.
W = parameters.Conv.Weights;
B = parameters.Conv.Bias;
dlY = dlconv(dlX,W,B);

% Batch normalization. Update batch normalization state when training.
offset = parameters.BatchNorm.Offset;
scale = parameters.BatchNorm.Scale;
trainedMean = state.BatchNorm.TrainedMean;
trainedVariance = state.BatchNorm.TrainedVariance;
if isTraining
    [dlY,trainedMean,trainedVariance] = batchnorm(dlY,offset,scale,trainedMean,trainedVariance);
    
    % Update state.
    state.BatchNorm.TrainedMean = trainedMean;
    state.BatchNorm.TrainedVariance = trainedVariance;
else
    dlY = batchnorm(dlY,offset,scale,trainedMean,trainedVariance);
end

% ReLU.
dlY = relu(dlY);
end

Функция преобразования данных

dataTransform функция обрабатывает входные данные dlX использование совместно используемой MLP, макс. операции и другой совместно используемой MLP для прогнозирования матрицы преобразования T. Матрица преобразования применяется к входу dlX с использованием операции умножения пакетной матрицы. Функция возвращает результат умножения пакетной матрицы и матрицы преобразования.

function [dlY,state,T] = dataTransform(dlX,parameters,state,isTraining)

% Shared MLP.
[dlY,state.Block1.Perceptron] = sharedMLP(dlX,parameters.Block1.Perceptron,state.Block1.Perceptron,isTraining);

% Max operation.
dlY = max(dlY,[],1);

% Shared MLP.
[dlY,state.Block2.Perceptron] = sharedMLP(dlY,parameters.Block2.Perceptron,state.Block2.Perceptron,isTraining);

% Transform net (T-Net). Apply last fully connected operation as W*X to
% predict tranformation matrix T.
dlY = squeeze(dlY); % N-by-B
T = parameters.Transform * stripdims(dlY); % K^2-by-B

% Reshape T into a square matrix.
K = sqrt(size(T,1));
T = reshape(T,K,K,1,[]); % [K K 1 B]
T = T + eye(K);

% Apply to input dlX using batch matrix multiply. 
[C,B] = size(dlX,[3 4]);  % [M 1 K B]
dlX = reshape(dlX,[],C,1,B); % [M K 1 B]
Y = pagemtimes(dlX,T);
dlY = dlarray(Y,"SCSB");
end

Функции инициализации параметров модели

initializeTransform Функция

initializeTransform принимает за вход размер канала и количество скрытых каналов для двух совместно используемых MLP и возвращает инициализированные параметры в структуре. Параметры инициализируются с помощью He weight initialization [3].

function [params,state] = initializeTransform(inputChannelSize,block1,block2)
[params.Block1,state.Block1] = initializeSharedMLP(inputChannelSize,block1);
[params.Block2,state.Block2] = initializeSharedMLP(block1(end),block2);

% Parameters for the transform matrix.
params.Transform = dlarray(zeros(inputChannelSize^2,block2(end)));
end

initializeSharedMLP Функция

Функция initialitySharedMLP принимает в качестве входных значений размер канала и скрытый размер канала и возвращает инициализированные параметры в структуре. Параметры инициализируются с помощью инициализации He weight.

function [params,state] = initializeSharedMLP(inputChannelSize,hiddenChannelSize)
weights = initializeWeightsHe([1 1 inputChannelSize hiddenChannelSize(1)]);
bias = zeros(hiddenChannelSize(1),1,"single");
p.Conv.Weights = dlarray(weights);
p.Conv.Bias = dlarray(bias);

p.BatchNorm.Offset = dlarray(zeros(hiddenChannelSize(1),1,"single"));
p.BatchNorm.Scale = dlarray(ones(hiddenChannelSize(1),1,"single"));

s.BatchNorm.TrainedMean = zeros(hiddenChannelSize(1),1,"single");
s.BatchNorm.TrainedVariance = ones(hiddenChannelSize(1),1,"single");

params.Perceptron(1) = p;
state.Perceptron(1) = s;

for k = 2:numel(hiddenChannelSize)
    weights = initializeWeightsHe([1 1 hiddenChannelSize(k-1) hiddenChannelSize(k)]);
    bias = zeros(hiddenChannelSize(k),1,"single");
    p.Conv.Weights = dlarray(weights);
    p.Conv.Bias = dlarray(bias);
    
    p.BatchNorm.Offset = dlarray(zeros(hiddenChannelSize(k),1,"single"));
    p.BatchNorm.Scale = dlarray(ones(hiddenChannelSize(k),1,"single"));
    
    s.BatchNorm.TrainedMean = zeros(hiddenChannelSize(k),1,"single");
    s.BatchNorm.TrainedVariance = ones(hiddenChannelSize(k),1,"single");

    params.Perceptron(k) = p;
    state.Perceptron(k) = s;
end
end

initializeClassificationMLP Функция

initializeClassificationMLP функция принимает в качестве входного значения размер канала, размер скрытого канала и количество классов и возвращает инициализированные параметры в структуре. Совместно используемый MLP инициализируется с помощью He weight initialization, а окончательная полностью подключенная операция инициализируется с использованием случайных гауссовых значений.

function [params,state] = initializeClassificationMLP(inputChannelSize,hiddenChannelSize,numClasses)
[params,state] = initializeSharedMLP(inputChannelSize,hiddenChannelSize);

weights = initializeWeightsGaussian([numClasses hiddenChannelSize(end)]);
bias = zeros(numClasses,1,"single");
params.FC.Weights = dlarray(weights);
params.FC.Bias = dlarray(bias);
end

initializeWeightsHe Функция

initializeWeightsHe инициализирует параметры с помощью инициализации He.

function x = initializeWeightsHe(sz)
fanIn = prod(sz(1:3));
stddev = sqrt(2/fanIn);
x = stddev .* randn(sz);
end

initializeWeightsGaussian Функция

initializeWeightsGaussian функция инициализирует параметры, используя гауссову инициализацию со стандартным отклонением 0,01.

function x = initializeWeightsGaussian(sz)
x = randn(sz,"single") .* 0.01;
end

Функции предварительной обработки данных

preprocessPointCloudData Функция

preprocessPointCloudData функция извлекает данные точек X, Y, Z из входных данных и нормализует данные между 0 и 1. Функция возвращает нормализованные данные X, Y, Z.

function data = preprocessPointCloud(data)

if ~iscell(data)
    data = {data};
end

numObservations = size(data,1);
for i = 1:numObservations
    % Scale points between 0 and 1.
    xlim = data{i,1}.XLimits;
    ylim = data{i,1}.YLimits;
    zlim = data{i,1}.ZLimits;
    
    xyzMin = [xlim(1) ylim(1) zlim(1)];
    xyzDiff = [diff(xlim) diff(ylim) diff(zlim)];
    
    data{i,1} = (data{i,1}.Location - xyzMin) ./ xyzDiff;
end
end

selectPoints Функция

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

function data = selectPoints(data,numPoints) 
% Select the desired number of points by downsampling or replicating
% point cloud data.
numObservations = size(data,1);
for i = 1:numObservations    
    ptCloud = data{i,1};
    if ptCloud.Count > numPoints
        percentage = numPoints/ptCloud.Count;
        data{i,1} = pcdownsample(ptCloud,"random",percentage);   
    else    
        replicationFactor = ceil(numPoints/ptCloud.Count);
        ind = repmat(1:ptCloud.Count,1,replicationFactor);
        data{i,1} = select(ptCloud,ind(1:numPoints));
    end 
end
end

Функции увеличения объема данных

augmentPointCloudData функция случайным образом поворачивает облако точек вокруг оси z, случайным образом сбрасывает 30% точек и случайным образом искажает положение точки гауссовым шумом.

function data = augmentPointCloud(data)
   
numObservations = size(data,1);
for i = 1:numObservations
    
    ptCloud = data{i,1};
    
    % Rotate the point cloud about "up axis", which is Z for this data set.
    tform = randomAffine3d(...
        "XReflection", true,...
        "YReflection", true,...
        "Rotation",@randomRotationAboutZ);
    
    ptCloud = pctransform(ptCloud,tform);
    
    % Randomly drop out 30% of the points.
    if rand > 0.5
        ptCloud = pcdownsample(ptCloud,'random',0.3);
    end
    
    if rand > 0.5
        % Jitter the point locations with Gaussian noise with a mean of 0 and 
        % a standard deviation of 0.02 by creating a random displacement field.
        D = 0.02 * randn(size(ptCloud.Location));
        ptCloud = pctransform(ptCloud,D);   
    end
    
    data{i,1} = ptCloud;
end
end

function [rotationAxis,theta] = randomRotationAboutZ()
rotationAxis = [0 0 1];
theta = 2*pi*rand;
end

Вспомогательные функции

aggregateConfusionMetric Функция

aggregateConfusionMetric функция постепенно заполняет матрицу путаницы на основе прогнозируемых результатов YPred и ожидаемые результаты YTest.

function cmat = aggreateConfusionMetric(cmat,YTest,YPred)
YTest = gather(extractdata(YTest));
YPred = gather(extractdata(YPred));
[m,n] = size(cmat);
cmat = cmat + full(sparse(YTest,YPred,1,m,n));
end

initializeTrainingProgressPlot Функция

initializeTrainingProgressPlot функция настраивает два графика для отображения потерь при обучении, точности обучения и точности проверки.

function [plotter,trainAccPlotter,valAccPlotter] = initializeTrainingProgressPlot()
% Plot the loss, training accuracy, and validation accuracy.
figure

% Loss plot
subplot(2,1,1)
plotter = animatedline;
xlabel("Iteration")
ylabel("Loss")

% Accuracy plot
subplot(2,1,2)
trainAccPlotter = animatedline('Color','b');
valAccPlotter = animatedline('Color','g');
legend('Training Accuracy','Validation Accuracy','Location','northwest');
xlabel("Iteration")
ylabel("Accuracy")
end

replicateFiles Функция

replicateFiles функция случайным образом переполняет набор файлов и возвращает набор файлов с numDesired элементы.

function files = randReplicateFiles(files,numDesired)
n = numel(files);
ind = randi(n,numDesired,1);
files = files(ind);
end

downloadSydneyUrbanObjects Функция

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

function datapath = downloadSydneyUrbanObjects(dataLoc)

if nargin == 0
    dataLoc = pwd;
end

dataLoc = string(dataLoc);

url = "http://www.acfr.usyd.edu.au/papers/data/";
name = "sydney-urban-objects-dataset.tar.gz";

datapath = fullfile(dataLoc,'sydney-urban-objects-dataset');
if ~exist(datapath,'dir')
    disp('Downloading Sydney Urban Objects data set...');
    untar(url+name,dataLoc);
end

end

loadSydneyUrbanObjectsData Функция

The loadSydneyUrbanObjectsData создает хранилище данных для загрузки данных облака точек и меток из набора данных Sydney Urban Objects.

function ds = loadSydneyUrbanObjectsData(datapath,folds)

if nargin == 0
    return;
end

if nargin < 2
    folds = 1:4;
end

datapath = string(datapath);
path = fullfile(datapath,'objects',filesep);

% Add folds to datastore.
foldNames{1} = importdata(fullfile(datapath,'folds','fold0.txt'));
foldNames{2} = importdata(fullfile(datapath,'folds','fold1.txt'));
foldNames{3} = importdata(fullfile(datapath,'folds','fold2.txt'));
foldNames{4} = importdata(fullfile(datapath,'folds','fold3.txt'));
names = foldNames(folds);
names = vertcat(names{:});

fullFilenames = append(path,names);
ds = fileDatastore(fullFilenames,'ReadFcn',@extractTrainingData,'FileExtensions','.bin');
end

batchData Функция

batchData функция сопоставляет данные в пакеты и перемещает данные в графический процессор для обработки.

function [X,Y] = batchData(ptCloud,labels)
X = cat(4,ptCloud{:});
labels = cat(1,labels{:});
Y = onehotencode(labels,2);
end

extractTrainingData Функция

Функция extractTrainingData извлекает данные облака точек и метки из набора данных Sydney Urban Objects.

function dataOut = extractTrainingData(fname)

[pointData,intensity] = readbin(fname);

[~,name] = fileparts(fname);
name = string(name);
name = extractBefore(name,'.');
name = replace(name,'_',' ');

labelNames = ["4wd","building","bus","car","pedestrian","pillar",...
    "pole","traffic lights","traffic sign","tree","truck","trunk","ute","van"];

label = categorical(name,labelNames);

dataOut = {pointCloud(pointData,'Intensity',intensity),label};

end

readbin Функция

readbin считывает данные облака точек из двоичных файлов объекта Sydney Urban Object.

function [pointData,intensity] = readbin(fname)
% readbin Read point and intensity data from Sydney Urban Object binary
% files.

% names = ['t','intensity','id',...
%          'x','y','z',...
%          'azimuth','range','pid']
% 
% formats = ['int64', 'uint8', 'uint8',...
%            'float32', 'float32', 'float32',...
%            'float32', 'float32', 'int32']

fid = fopen(fname, 'r');
c = onCleanup(@() fclose(fid));

fseek(fid,10,-1); % Move to the first X point location 10 bytes from beginning
X = fread(fid,inf,'single',30);
fseek(fid,14,-1);
Y = fread(fid,inf,'single',30);
fseek(fid,18,-1);
Z = fread(fid,inf,'single',30);

fseek(fid,8,-1);
intensity = fread(fid,inf,'uint8',33);

pointData = [X,Y,Z];
end

downloadPretrainedPointNet Функция

downloadPretrainedPointNet загружает предварительно подготовленную модель точечной сети.

function data = downloadPretrainedPointNet(pretrainedURL)
% Download and load a pretrained pointnet model.
if ~exist('pointnetSydneyUrbanObjects.mat', 'file')
    if ~exist('pointnetSydneyUrbanObjects.zip', 'file')
        disp('Downloading pretrained detector (5 MB)...');
        websave('pointnetSydneyUrbanObjects.zip', pretrainedURL);
    end
    unzip('pointnetSydneyUrbanObjects.zip');
end
data = load("pointnetSydneyUrbanObjects.mat");
end

prepareForPrediction Функция

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

function p = prepareForPrediction(p,fcn)

for i = 1:numel(p)
    p(i) = structfun(@(x)invoke(fcn,x),p(i),'UniformOutput',0);
end

    function data = invoke(fcn,data)
        if isstruct(data)
            data = prepareForPrediction(data,fcn);
        else
            data = fcn(data);
        end
    end
end

% Move data to the GPU.
function x = toDevice(x,useGPU)
if useGPU
    x = gpuArray(x);
end
end

Ссылки

[1] Чарльз, Р. Ци, Хао Су, Мо Кайчун и Леонидас Дж. Гибас. «StartNet: глубокое обучение наборам точек для классификации и сегментации 3D». В 2017 году Конференция IEEE по компьютерному зрению и распознаванию образов (CVPR), 77-85. Гонолулу, HI: IEEE, 2017. https://doi.org/10.1109/CVPR.2017.16.

[2] де Дьюж, Марк, Аластер Квадрас, Кальвин Хунг и Бертран Дуйяр. «Неуправляемое обучение функциям классификации наружных сканирований 3D». В Австралазийской конференции по робототехнике и автоматизации 2013 (ACRA 13). Сидней, Австралия: АКРА, 2013.

[3] Хэ, Каймин, Сянъу Чжан, Шаоцин Жэнь и Цзянь Сунь. «Углубляясь в выпрямители: превосходя показатели на уровне человека по классификации ImageNet». В 2015 году Международная конференция IEEE по компьютерному зрению (ICCV), 1026-34. Сантьяго, Чили: IEEE, 2015. https://doi.org/10.1109/ICCV.2015.123.

Связанные темы