В этом примере показано, как обнаружить и локализовать аномалии, такие как трещины в конкретной использующей объяснимой классификации единых классов.
В подходах одного класса к обнаружению аномалии обучение полуконтролируется, означая, что сеть обучается на данных, состоящих только из примеров изображений без аномалий [1]. Несмотря на обучение на выборках только нормальных сцен, модель изучает, как различать нормальные и anamalous сцены. Изучение одного класса предлагает много преимуществ для проблем обнаружения аномалии:
Представления аномалий могут быть недостаточными.
Аномалии могут представлять дорогие или catastophic результаты.
Может быть много видов аномалий, и виды аномалий могут переключить время жизни модели. Описание, какие "хорошие" взгляды как, чем часто более выполнимы, чем обеспечение данных, которые представляют все возможные аномалии в настройках реального мира.
Ключевая роль обнаружения аномалии для наблюдателя - человека, чтобы смочь изучить, почему обучивший сеть классифицирует изображения как аномалии. Объяснимая классификация добавляет предсказание класса с информацией, которая выравнивает по ширине, как нейронная сеть достигла своего решения классификации.
Этот пример исследует, как глубокое обучение одного класса может использоваться, чтобы создать точные классификаторы обнаружения аномалии. Пример также реализует объяснимую классификацию с помощью сети, которая возвращает тепловую карту с вероятностью, что каждый пиксель является аномальным.
Этот пример работает с Конкретными Взломанными Изображениями для набора данных Классификации. Набор данных содержит изображения двух классов: Negative
изображения без трещин, существующих на дороге и Positive
изображения с трещинами. Набор данных обеспечивает 20 000 изображений каждого класса. Размер набора данных составляет 235 Мбайт.
Установите dataDir
как желаемое местоположение набора данных.
dataDir = fullfile(tempdir,"ConcreteCracks"); if ~exist(dataDir,"dir") mkdir(dataDir); end
Чтобы загрузить набор данных, перейдите к этой ссылке: https://md-datasets-cache-zipfiles-prod.s3.eu-west-1.amazonaws.com/5y9wdsg2zt-2.zip. Извлеките zip-файл, чтобы получить файл RAR, затем извлеките содержимое файла RAR в директорию, заданную dataDir
переменная. Когда извлечено успешно, dataDir
содержит два подкаталога: Negative
и Positive
.
Создайте imageDatastore
это читает и управляет данными изображения. Пометьте каждое изображение как Positive
или Negative
согласно имени его директории.
imdsP = imageDatastore(fullfile(dataDir,"Positive"),LabelSource="foldernames"); imdsN = imageDatastore(fullfile(dataDir,"Negative"),LabelSource="foldernames");
Отобразите пример каждого класса. Отобразите отрицательное, или хорошее, изображение без взломанных аномалий слева. В хорошем изображении малы недостатки и отклонения в структуре. Отобразите положительное, или аномальный, отобразите справа. Аномальное изображение показывает большую черную трещину, ориентированную вертикально.
samplePositive = preview(imdsP);
sampleNegative = preview(imdsN);
montage({sampleNegative,samplePositive})
title("Road Images Without (Left) and With (Right) Crack Anomalies")
Чтобы симулировать более типичный полуконтролируемый рабочий процесс, создайте набор обучающих данных только 250 хороших изображений. Включайте небольшую коллекцию аномальных учебных изображений, чтобы обеспечить лучшие результаты классификации. Создайте сбалансированный набор тестов с помощью 1 000 изображений от каждого класса. Создайте сбалансированный набор валидации с помощью 100 изображений каждого класса.
numTrainNormal = 250; numTrainAnomaly = 10; numTest = 1000; numVal = 100; [dsTrainPos,dsTestPos,dsValPos,~] = splitEachLabel(imdsP,numTrainAnomaly,numTest,numVal); [dsTrainNeg,dsTestNeg,dsValNeg,~] = splitEachLabel(imdsN,numTrainNormal,numTest,numVal); dsTrain = imageDatastore(cat(1,dsTrainPos.Files,dsTrainNeg.Files),LabelSource="foldernames"); dsTest = imageDatastore(cat(1,dsTestPos.Files,dsTestNeg.Files),LabelSource="foldernames"); dsVal = imageDatastore(cat(1,dsValPos.Files,dsValNeg.Files),LabelSource="foldernames");
Задайте анонимную функцию, addLabelFcn
, это создает одногорячее закодированное представление информации о метке от входного изображения. Затем преобразуйте хранилища данных с помощью transform
функционируйте таким образом, что хранилища данных возвращают массив ячеек данных изображения и соответствующего одногорячего закодированного массива. transform
функция применяет операции, заданные анонимным addLabelFcn
функция.
addLabelFcn = @(x,info) deal({x,onehotencode(info.Label,1)},info); dsTrain = transform(dsTrain,addLabelFcn,IncludeInfo=true); dsVal = transform(dsVal,addLabelFcn,IncludeInfo=true); dsTest = transform(dsTest,addLabelFcn,IncludeInfo=true);
Увеличьте обучающие данные при помощи transform
функция с пользовательскими операциями предварительной обработки, заданными помощником, функционирует augmentDataForConcreteClassification
. Функция помощника присоединена к примеру как к вспомогательному файлу.
augmentDataForConcreteClassification
функция случайным образом применяет 90 вращений степени и горизонтальное и вертикальное отражение к каждому входному изображению.
dsTrain = transform(dsTrain,@augmentDataForConcreteClassification);
Создайте minibatchqueue
Объект (Deep Learning Toolbox), который справляется с мини-пакетной обработкой наблюдений в пользовательском учебном цикле. minibatchqueue
возразите также бросает данные к dlarray
Объект (Deep Learning Toolbox), который включает автоматическое дифференцирование в применении глубокого обучения.
Задайте мини-пакетный формат экстракции данных как "SSCB"
(пространственный, пространственный, канал, пакет). Установите DispatchInBackground
аргумент значения имени как boolean, возвращенный canUseGPU
. Если поддерживаемый графический процессор доступен для расчета, то minibatchqueue
объект предварительно обрабатывает мини-пакеты в фоновом режиме в параллельном пуле во время обучения.
mbSize = 128; mbqTrain = minibatchqueue(dsTrain,PartialMiniBatch="discard", ... MiniBatchFormat=["SSCB","CB"],MiniBatchSize=mbSize); mbqVal = minibatchqueue(dsVal,MiniBatchFormat=["SSCB","CB"],MiniBatchSize=mbSize);
Вычислите среднее значение на канал учебных изображений для использования в нулевой средней нормализации.
queue = copy(mbqTrain); queue.PartialMiniBatch = "return"; X = next(queue); sumImg = sum(X,4); while hasdata(queue) X = next(queue); sumImg = sumImg + sum(X,4); end trainSetMean = sumImg ./ dsTrain.numpartitions; trainSetMean = mean(trainSetMean,[1 2]);
Этот пример использует модель [1] полностью сверточного описания данных (FCDD). Основная идея о FCDD состоит в том, чтобы обучить сеть, чтобы произвести тепловую карту из входного изображения.
Этот пример использует сеть VGG-16 [3] обученный на ImageNet [4] как основная полностью сверточная сетевая архитектура. Пример замораживает большинство модели и случайным образом инициализирует и обучает итоговые сверточные этапы. Этот подход включает быстрое обучение с небольшими количествами входных обучающих данных.
vgg16
(Deep Learning Toolbox) функция возвращает предварительно обученную сеть VGG-16. Эта функция требует Модели Deep Learning Toolbox™ для пакета Сетевой поддержки VGG-16. Если этот пакет поддержки не установлен, то функция обеспечивает ссылку на загрузку.
pretrainedVGG = vgg16;
Заморозьте первые 24 слоя сети путем установки весов и смещения к 0
.
numLayersToFreeze = 24; net = pretrainedVGG.Layers(1:numLayersToFreeze); for idx = 1:numLayersToFreeze if isprop(net(idx),"Weights") net(idx) = setLearnRateFactor(net(idx),Weights=0); net(idx) = setLearnRateFactor(net(idx),Bias=0); end end
Добавьте итоговый сверточный этап. Этот этап похож на следующий сверточный этап VGG-16, но со случайным образом инициализированными и обучаемыми сверточными слоями и с нормализацией партии. Итоговая свертка 1 на 1 сжимает сетевой выход в тепловую карту с одним каналом. Последний слой является функцией потерь Псеудо-Хубера, используемой, чтобы стабилизировать обучение с потерей FCDD 1[] 2[].
finalLayers = [ convolution2dLayer(3,512,Padding="same") batchNormalizationLayer reluLayer convolution2dLayer(3,512,Padding="same") batchNormalizationLayer reluLayer convolution2dLayer(1,1) functionLayer(@(x)sqrt(x.^2+1)-1,Name="pseudoHuber")];
Соберите полную сеть.
net = dlnetwork([net;finalLayers]); lgraph = layerGraph(net);
Замените входной слой изображений в энкодере с новым входным слоем, который выполняет нулевую центральную нормализацию с помощью вычисленного среднего значения. Установите входной размер сети, равной размеру изображений в наборе данных.
inputSize = size(sampleNegative); newInputLayer = imageInputLayer(inputSize,Normalization="zerocenter", ... Mean=extractdata(trainSetMean),Name="inputLayer"); lgraph = replaceLayer(lgraph,lgraph.Layers(1).Name,newInputLayer);
Задайте опции обучения для оптимизации Адама. Обучите сеть в течение 70 эпох.
learnRate = 1e-4; averageGrad = []; averageSqGrad = []; numEpochs = 70;
По умолчанию этот пример загружает предварительно обученную версию сети VGG-16 с помощью функции помощника downloadTrainedConcreteClassificationNet
. Предварительно обученная сеть может использоваться, чтобы запустить целый пример, не ожидая обучения завершиться.
Чтобы обучить сеть, установите doTraining
переменная в следующем коде к true
. Обучите модель в пользовательском учебном цикле. Для каждой итерации:
Считайте данные для текущего мини-пакета с помощью next
(Deep Learning Toolbox) функция.
Оцените градиенты модели с помощью dlfeval
(Deep Learning Toolbox) функция и modelGradients
функция помощника. Эта функция задана в конце этого примера.
Обновите сетевые параметры с помощью adamupdate
(Deep Learning Toolbox) функция.
Обновите график потери.
Обучайтесь на одном или нескольких графических процессорах при наличии. Используя графический процессор требует Parallel Computing Toolbox™, и CUDA® включил NVIDIA® графический процессор. Для получения дополнительной информации смотрите Поддержку графического процессора Релизом (Parallel Computing Toolbox). Обучение занимает приблизительно 7 минут на Титане NVIDIA™ X.
doTraining = false; if doTraining [hFig,batchLine] = initializeTrainingPlotConcreteClassification; start = tic; iteration = 0; for epoch = 1:numEpochs reset(mbqTrain); shuffle(mbqTrain); while hasdata(mbqTrain) [X,T] = next(mbqTrain); [grad,loss,state] = dlfeval(@modelGradients,net,X,T); iteration = iteration + 1; [net,averageGrad,averageSqGrad] = adamupdate(net, ... grad,averageGrad,averageSqGrad,iteration,learnRate); net.State = state; % Update the plot updateTrainingPlotConcreteClassification(batchLine,iteration,loss,start,epoch); end end else trainedConcreteClassificationNet_url = 'https://www.mathworks.com/supportfiles/vision/data/trainedAnomalyDetectionNet.zip'; downloadTrainedConcreteClassificationNet(trainedConcreteClassificationNet_url,dataDir); load(fullfile(dataDir,"trainedAnomalyDetectionNet.mat")); end
Классифицируйте изображение как хорошее или аномальное использование тепловой карты аномалии, предсказанной сетью на двух шагах. Во-первых, создайте классификатор, который возвращает вероятность, что изображение имеет аномалию на основе тепловой карты, предсказанной обучившим сеть. Затем можно присвоить метку класса, чтобы протестировать изображения на основе вероятности, возвращенной классификатором.
Вычислите средний счет аномалии и известную метку основной истины (Positive
или Negative
) для каждого изображения в наборе валидации. Этот пример выбирает порог счета аномалии с помощью набора валидации, чтобы не пропускать информацию от набора тестов в проект классификатора.
reset(mbqVal); scores = zeros(dsVal.numpartitions,1); labels = zeros(dsVal.numpartitions,1); idx = 1; while hasdata(mbqVal) [X,T] = next(mbqVal); batchSize = size(X,4); Y = predict(net,X); Y = mean(Y,[1 2]); T = onehotdecode(T,[0 1],1,"double"); scores(idx:idx+batchSize-1) = Y; labels(idx:idx+batchSize-1) = T; idx = idx+batchSize; end
Соответствуйте сигмоидальному к баллам аномалии и сопоставленным меткам класса. Получившаяся анонимная функция, anomalyProbability
, берет входной счет аномалии, предсказанный сетью, и возвращает вероятность, что счет описывает аномалию. Выходные параметры от anomalyProbability
больше, чем 0,5 рассматриваются аномалиями.
coeff = glmfit(scores,labels,"binomial","link","logit"); sigmoid = @(x) 1./(1+exp(-x)); anomalyProbability = @(x) sigmoid(coeff(2).*x + coeff(1)); fplot(anomalyProbability) xlabel("Anomaly Score") ylabel("Probability of Anomaly")
Сигмоидальное значение по умолчанию имеет значение 0,5 в счете 0. Вычислите счет аномалии в который подходящее сигмоидальное, заданное anomalyProbability
функция имеет значение 0,5.
threshold = -coeff(1)/coeff(2)
threshold = 0.1567
Предскажите тепловую карту аномалии и вычислите средний счет аномалии к каждому изображению в наборе тестов. Также получите метки основной истины каждого тестового изображения.
q = minibatchqueue(dsTest,MiniBatchSize=64,MiniBatchFormat=["SSCB","CB"]); scores = zeros(dsTest.numpartitions,1); labels = zeros(dsTest.numpartitions,1); idx = 1; while hasdata(q) [X,T] = next(q); batchSize = size(X,4); Y = predict(net,X); Y = mean(Y,[1 2]); T = onehotdecode(T,[0 1],1,"double"); scores(idx:idx+batchSize-1) = Y; labels(idx:idx+batchSize-1) = T; idx = idx+batchSize; end
Предскажите метки класса для набора тестов с помощью модели классификации, заданной anomalyProbability
функция.
predictedLabels = anomalyProbability(scores) > 0.5;
Вычислите матрицу беспорядка и точность классификации для набора тестов. Модель классификации в этом примере очень точна и предсказывает небольшой процент ложных положительных сторон и ложных отрицательных сторон.
targetLabels = logical(labels); M = confusionmat(targetLabels,predictedLabels); confusionchart(M,["Negative","Positive"]) acc = sum(diag(M)) / sum(M,'all'); title(sprintf("Accuracy: %g",acc));
Выберите и отобразите изображение правильно классифицированной аномалии. Этим результатом является истинная положительная классификация.
idxTruePositive = find(targetLabels & predictedLabels,1);
dsExample = subset(dsTest,idxTruePositive);
dataTP = preview(dsExample);
imgTP = dataTP{1};
imshow(imgTP)
title("True Positive Test Image")
Получите тепловую карту изображения аномалии путем вызова predict
(Deep Learning Toolbox) функция на обучившем сеть. Сеть возвращает тепловую карту с меньшим размером, чем входное изображение, поэтому измените размер тепловой карты к размеру входного изображения.
hmapTP = predict(net,gpuArray(dlarray(single(imgTP),"SSCB")));
hmapTP = gather(extractdata(hmapTP));
hmapTP = anomalyProbability(hmapTP);
hmapTP = imresize(hmapTP,OutputSize=size(imgTP,[1 2]));
Отобразите наложение цветной тепловой карты на полутоновом представлении цветного изображения с помощью heatmapOverlay
функция помощника. Эта функция задана в конце примера. Настройте непрозрачность наложенной тепловой карты установкой alpha
к номеру в области значений [0, 1]. Для полностью непрозрачной тепловой карты задайте alpha
как 1
. Для полностью прозрачной тепловой карты задайте alpha
как 0
.
alpha = 0.4;
imshow(heatmapOverlay(imgTP,hmapTP,alpha))
title("Heatmap Overlay of True Positive Result")
Чтобы количественно подтвердить результат, отобразите средний счет аномалии тепловой карты истинного положительного тестового изображения, как предсказано сетью.
disp("Mean heatmap anomaly score of test image: "+scores(idxTruePositive(1)))
Mean heatmap anomaly score of test image: 1.2185
Выберите и отобразите изображение правильно классифицированного нормального изображения. Этим результатом является истинная отрицательная классификация.
idxTrueNegative = find(~targetLabels & ~predictedLabels);
dsTestTN = subset(dsTest,idxTrueNegative);
dataTN = read(dsTestTN);
imgTN = dataTN{1};
imshow(imgTN)
title("True Negative Test Image")
Получите тепловую карту правильно предсказанного негатива путем вызова predict
(Deep Learning Toolbox) функция на обучившем сеть. Измените размер тепловой карты к размеру входного изображения.
hmapTN = predict(net,gpuArray(dlarray(single(imgTN),"SSCB")));
hmapTN = gather(extractdata(hmapTN));
hmapTN = anomalyProbability(hmapTN);
hmapTN = imresize(hmapTN,OutputSize=size(imgTN,[1 2]));
Отобразите наложение цветной тепловой карты на полутоновом представлении цветного изображения с помощью heatmapOverlay
функция помощника. Эта функция задана в конце примера. Много истинных отрицательных тестовых изображений, таких как это тестовое изображение, или имеют небольшие баллы аномалии через целое изображение или большие баллы аномалии в локализованном фрагменте изображения.
imshow(heatmapOverlay(imgTN,hmapTN,alpha))
title("Heatmap Overlay of True Negative Result")
Отобразите средний счет аномалии тепловой карты истинного отрицательного тестового изображения, как предсказано сетью.
disp("Mean heatmap anomaly score of test image: "+scores(idxTrueNegative(1)))
Mean heatmap anomaly score of test image: 0.0022878
Ложные положительные стороны являются изображениями без взломанных аномалий, которые сеть классифицирует как аномальные. Отобразите любые ложные позитивные изображения от набора тестов в монтаже.
falsePositiveIdx = find(predictedLabels & ~targetLabels); dataFP = readall(subset(dsTest,falsePositiveIdx)); numFP = length(falsePositiveIdx); if numFP>0 montage(dataFP(:,1),Size=[1,numFP]); title("False Positives in Test Set") end
Используйте объяснение от сети, чтобы получить информацию в misclassifications.
Вычислите оверлейные программы тепловой карты.
hmapOverlay = cell(1,numFP); for idx = 1:numFP img = dataFP{idx,1}; map = predict(net,gpuArray(dlarray(single(img),"SSCB"))); map = gather(extractdata(map)); mapProb = anomalyProbability(map); hmap = imresize(mapProb,OutputSize=size(img,[1 2])); hmapOverlay{idx} = heatmapOverlay(img,hmap,alpha); end
Ложные позитивные изображения показывают функции, такие как скалы, которые имеют подобные визуальные характеристики к трещинам. Во всех случаях это трудные проблемы классификации, в которых объяснимая природа сетевой архитектуры полезна для получения информации.
if numFP>0 montage(hmapOverlay,Size=[1,numFP]) title("Heatmap Overlays of False Positive Results") end
Отобразите среднее множество тепловой карты аномалии ложных положительных тестовых изображений, как предсказано сетью. Средние баллы больше, чем порог, используемый моделью классификации, приводящей к misclassifications.
disp("Mean heatmap anomaly scores:"); scores(falsePositiveIdx)
Mean heatmap anomaly scores:
ans = 4×1
0.3218
0.1700
0.2516
0.1862
Ложные отрицательные стороны являются изображениями с аномалиями, которые сеть классифицирует как хорошие. Отобразите любые ложные негативы от набора тестов в монтаже. Ложные негативы показывают относительно тонкие видимые трещины.
falseNegativeIdx = find(~predictedLabels & targetLabels); dataFN = readall(subset(dsTest,falseNegativeIdx)); numFN = length(falseNegativeIdx); if numFN>0 montage(dataFN(:,1),Size=[1,numFN]) title("False Negatives in Test Set") end
Вычислите оверлейные программы тепловой карты.
hmapOverlay = cell(1,numFN); for idx = 1:numFN img = dataFN{idx,1}; map = predict(net,gpuArray(dlarray(single(img),"SSCB"))); map = gather(extractdata(map)); mapProb = anomalyProbability(map); hmap = imresize(mapProb,OutputSize=size(img,[1 2])); hmapOverlay{idx} = heatmapOverlay(img,hmap,alpha); end
Отобразите оверлейные программы тепловой карты. Сеть присваивает большой счет аномалии вдоль трещин, как ожидалось.
if numFN>0 montage(hmapOverlay,Size=[1,numFN]) title("Heatmap Overlays of False Negative Results") end
Отобразите среднее множество аномалии тепловой карты отрицательных положительных тестовых изображений, как предсказано сетью. Средние баллы меньше, чем порог, используемый сигмоидальной моделью классификации, приводящей к misclassifications.
disp("Mean heatmap anomaly scores:"); scores(falseNegativeIdx)
Mean heatmap anomaly scores:
ans = 4×1
0.1477
0.1186
0.1241
0.0920
modelGradients
функция помощника вычисляет градиенты и потерю для сети.
function [grad,loss,state] = modelGradients(net,X,T) [Y,state] = forward(net,X); loss = fcddLoss(Y,T); grad = dlgradient(loss,net.Learnables); end
fcddLoss
функция помощника вычисляет потерю FDCC.
function loss = fcddLoss(Y,T) normalTerm = mean(Y,[1 2 3]); anomalyTerm = log(1-exp(-normalTerm)); isNegative = T(1,:) == 1; loss = mean(single(isNegative) .* normalTerm-single(~isNegative) .* anomalyTerm); end
heatmapOverlay
функция помощника накладывает цветную тепловую карту hmap
на полутоновом представлении изображения img
. Настройте прозрачность наложения тепловой карты путем определения alpha
аргумент как номер в области значений [0, 1].
function out = heatmapOverlay(img,hmap,alpha) % Normalize to the range [0, 1] img = mat2gray(img); hmap = mat2gray(hmap); % Convert image to a 3-channel grayscale representation imgGray = im2gray(img); imgGray = repmat(imgGray,[1 1 3]); % Convert heatmap to an RGB image using a colormap cmap = parula(256); hmapRGB = ind2rgb(gray2ind(hmap,size(cmap,1)),cmap); % Blend results hmapWeight = alpha; grayWeight = 1-hmapWeight; out = im2uint8(grayWeight.*imgGray + hmapWeight.*hmapRGB); end
[1] Лизнерский, Филипп, Лукаш Руфф, Роберт А. Вэндермеулен, Билли Джо Фрэнкс, Мариус Клофт и Клаус-Роберт Мюллер. "Объяснимая Глубокая Классификация Одного класса". Предварительно распечатайте, представленный 18 марта 2021. https://arxiv.org/abs/2007.01760.
[2] Ерш, Лукаш, Роберт А. Вэндермеулен, Билли Джо Фрэнкс, Клаус-Роберт Мюллер и Мариус Клофт. "Заново продумав Предположения в Глубоком Обнаружении Аномалии". Предварительно распечатайте, submitte, 30 мая 2020. https://arxiv.org/abs/2006.00339.
[3] Симонян, Карен и Эндрю Зиссермен. "Очень Глубоко Сверточные Сети для Крупномасштабного Распознавания Изображений". Предварительно распечатайте, представленный 10 апреля 2015. https://arxiv.org/abs/1409.1556.
[4] ImageNet. https://www.image-net.org.
transform
| dlarray
(Deep Learning Toolbox) | dlfeval
(Deep Learning Toolbox) | adamupdate
(Deep Learning Toolbox) | minibatchqueue
(Deep Learning Toolbox)