В этом примере показано, как обучить сеть PointNet для классификации облака точек.
Данные облака точек получаются различными датчиками, такими как лидар, радар и камеры глубины. Эти датчики захватывают 3-D информацию о положении объектов в сцене, что полезно для многих приложений в автономном управлении автомобилем и дополненной реальности. Например, различение транспортных средств от пешеходов имеет решающее значение для планирования пути автономного транспортного средства. Однако подготовка устойчивых классификаторов с данными облака точек является сложной проблемой из-за разреженности данных по объекту, окклюзий объектов и шума датчиков. Было показано, что методы глубокого обучения решают многие из этих проблем, изучая устойчивые представления функций непосредственно из данных облака точек. Одним из основополагающих методов глубокого обучения для классификации облака точек является PointNet [1].
Этот пример обучает классификатор PointNet на наборе данных 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 дополнительно состоит из четырех моделей, за которыми следует операция max.
Вход преобразования
Общая модель MLP
Модель преобразования функций
Общая модель MLP
Общая модель MLP реализована с помощью серии операций свертки, нормализации партии . и ReLU. Операция свертки сконфигурирована таким образом, чтобы веса разделялись между входом облаком точек. Модель преобразования состоит из общей MLP и усвояемой матрицы преобразования, которая применяется к каждому облаку точек. Общая MLP и операция max делают энкодер PointNet инвариантным порядку, в котором обрабатываются точки, в то время как модель преобразования обеспечивает инвариантность изменениям ориентации.
Общие модели MLP и преобразования параметризованы количеством входа каналов и скрытыми размерами каналов. Значения, выбранные в этом примере, выбираются путем настройки этих гиперпараметров на наборе данных Sydney Urban Objects. Обратите внимание, что если необходимо применить PointNet к другому набору данных, необходимо выполнить дополнительную настройку гиперпараметра.
Установите размер входного канала модели входного преобразования равный трем, а размер скрытого канала равным 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);
Модель классификатора PointNet состоит из общего MLP, полносвязной операции и активации softmax. Установите размер входа модели классификатора 64 и размер скрытого канала 512 и 256 и используйте initalizeClassifier
вспомогательная функция, перечисленная в конце этого примера, чтобы инициализировать параметры модели.
inputChannelSize = 64; hiddenChannelSize = [512,256]; numClasses = numel(classes); [parameters.ClassificationMLP, state.ClassificationMLP] = initializeClassificationMLP(inputChannelSize,hiddenChannelSize,numClasses);
Создайте функцию pointnetClassifier
, перечисленный в разделе Model Function в конце примера, чтобы вычислить выходы модели PointNet. Модель функции принимает за вход данные облака точек, параметры усвояемой модели, состояние модели и флаг, который определяет, возвращает ли модель выходы для обучения или предсказания. Сеть возвращает предсказания для классификации входного облака точек.
Создайте функцию modelGradients
, перечисленный в разделе Model Gradients Function примера, который принимает за вход параметры модели, состояние модели и мини-пакет входных данных и возвращает градиенты потерь относительно настраиваемых параметров в моделях и соответствующих потерь.
Обучите на 10 эпох и загрузите данные пакетами по 128. Установите начальную скорость обучения равную 0,002, а L2 коэффициент регуляризации равным 0,01.
numEpochs = 10; learnRate = 0.002; miniBatchSize = 128; l2Regularization = 0.01; learnRateDropPeriod = 15; learnRateDropFactor = 0.5;
Инициализируйте опции оптимизации Адама.
gradientDecayFactor = 0.9; squaredGradientDecayFactor = 0.999;
Обучите модель с помощью пользовательского цикла обучения.
Перетасуйте данные в начале обучения.
Для каждой итерации:
Считайте пакет данных.
Оцените градиенты модели.
Применить L2 регуляризацию веса.
Использование adamupdate
для обновления параметров модели.
Обновите график процесса обучения.
В конце каждой эпохи оценивайте модель по набору данных валидации и собирайте метрики неточности, чтобы измерить точность классификации по мере процессов обучения.
После завершения learnRateDropPeriod
эпох, уменьшите скорость обучения в множитель learnRateDropFactor
.
Инициализируйте скользящее среднее значение градиентов параметра и поэлементных квадратов градиентов, используемых оптимизатором Адама.
avgGradients = []; avgSquaredGradients = [];
Обучите модель, если doTraining
является true. В противном случае загружает предварительно обученную сеть
Обратите внимание, что обучение было проверено на NVIDIA Titan X с 12 ГБ памяти графический процессор. Если ваш графический процессор имеет меньше памяти, у вас может закончиться память во время обучения. Если это произойдет, опустите 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
вспомогательная функция. Для повышения робастности классификатора PointNet требуется дополнительное обучение.
Загрузка данных облака точек с помощью 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
, и настраиваемые параметры, и возвращает градиенты потери относительно настраиваемых параметров и соответствующих потерь. Потеря включает член регуляризации, разработанный, чтобы гарантировать, что матрица преобразования функций, предсказанная энкодером PointNet, приблизительно ортогональна. Чтобы вычислить градиенты, вычислите 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
The pointnetClassifier
функция принимает в качестве входов данные облака точек dlX, параметры усвояемой модели, состояние модели и флаг isTraining, который определяет, возвращает ли модель выходы для обучения или предсказания. Затем функция вызывает энкодер PointNet и многослойный перцептрон, чтобы извлечь классификационные функции. Во время обучения отсев наносят после каждой операции перцептрона. После последнего перцептрона a fullyconnect
операция преобразует классификационные функции в количество классов, и активация softmax используется для нормализации выхода в распределение вероятностей меток. Распределение вероятностей, состояние обновленной модели и матрица преобразования функций, предсказанная энкодером PointNet, возвращаются как выходы.
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
The 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
Функция perceptron обрабатывает вход 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
The dataTransform
функция обрабатывает входные dlX
использование общей MLP, операции max и другой общей 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
ФункцияThe initializeTransform
функция принимает за вход размер канала и количество скрытых каналов для двух общих MLP, и возвращает инициализированные параметры в struct. Параметры инициализируются с помощью инициализации веса He [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
ФункцияФункция initializeSharedMLP принимает на вход размер канала и размер скрытого канала и возвращает инициализированные параметры в struct. Параметры инициализируются с помощью инициализации веса He.
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
ФункцияThe initializeClassificationMLP
функция принимает за вход размер канала, размер скрытого канала и количество классов и возвращает инициализированные параметры в struct. Общая MLP инициализируется с помощью He-инициализации веса, а конечная полностью связанная операция инициализируется с помощью случайных Гауссовых значений.
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
ФункцияThe initializeWeightsHe
функция инициализирует параметры с помощью инициализации He.
function x = initializeWeightsHe(sz) fanIn = prod(sz(1:3)); stddev = sqrt(2/fanIn); x = stddev .* randn(sz); end
initializeWeightsGaussian
ФункцияThe initializeWeightsGaussian
функция инициализирует параметры с помощью Гауссовой инициализации со стандартным отклонением 0,01.
function x = initializeWeightsGaussian(sz) x = randn(sz,"single") .* 0.01; end
preprocessPointCloudData
ФункцияThe 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
ФункцияThe 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
The 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
ФункцияThe 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
ФункцияThe 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
ФункцияThe replicateFiles
функция случайным образом масштабирует набор файлов и возвращает набор файлов с numDesired
элементы.
function files = randReplicateFiles(files,numDesired) n = numel(files); ind = randi(n,numDesired,1); files = files(ind); end
downloadSydneyUrbanObjects
ФункцияThe 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
ФункцияThe 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
ФункцияThe readbin
функция считывает данные облака точек из двоичных файлов объекта Sydney Urban.
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
ФункцияThe downloadPretrainedPointNet
функция загружает предварительно обученную модель pointnet.
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
ФункцияThe 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] Charles, R. Qi, Hao Su, Mo Kaichun, and Leonidas J. Guibas. PointNet: Глубокое обучение on Point Sets for 3D Classification and Segmentation (неопр.) (недоступная ссылка). В 2017 году IEEE Conference on Компьютерное Зрение and Pattern Recognition (CVPR), 77-85. Гонолулу, HI: IEEE, 2017. https://doi.org/10.1109/CVPR.2017.16.
[2] de Deuge, Mark, Alastair Quadras, Calvin Hung и Bertrand Douillard. «Неконтролируемое обучение функциям для классификации сканов наружного 3D». В Австралийской конференции по робототехнике и автоматизации 2013 (ACRA 13). Сидней, Австралия: АКРА, 2013.
[3] He, Kaiming, Xiangyu Zhang, Shaoqing Ren, and Jian Sun. «Delving Deep Into Rectifiers: Overpassing Human-Level Performance on ImageNet Classification». В 2015 году IEEE International Conference on Компьютерное Зрение (ICCV), 1026-34. Сантьяго, Чили: IEEE, 2015. https://doi.org/10.1109/ICCV.2015.123.