Avtonom avtomashinani quvvatlantiradigan modelni tushunish va yaxshilash uchun DeepShap-dan foydalanish
Avtonom mashinalar meni dahshatga soladi. Agar biror narsa noto'g'ri bo'lsa, ularni to'xtata oladigan odamlarsiz katta metall parchalari uchib yuradi. Ushbu xavfni kamaytirish uchun bu hayvonlarni quvvatlantiradigan modellarni baholash etarli emas. Shuningdek, ular qanday bashorat qilishayotganini tushunishimiz kerak. Bu kutilmagan baxtsiz hodisalarga olib kelishi mumkin bo'lgan har qanday favqulodda holatlarning oldini olish uchun.
Xo'sh, bizning arizamiz unchalik muhim emas. Biz mini-avtomatlashtirilgan avtomashinani quvvatlantirish uchun ishlatiladigan modelni tuzatamiz (siz kutishingiz mumkin bo'lgan eng yomoni - bu jarohatlangan to'piq). Shunga qaramay, IML usullari foydali bo'lishi mumkin. Biz ular modelning ishlashini qanday yaxshilashi mumkinligini ko'rib chiqamiz.
Xususan, biz:
- Rasm ma'lumotlari va doimiy maqsadli o'zgaruvchi bilan PyTorch yordamida ResNet-18-ni nozik sozlang
- MSE va scatter chizmalari yordamida modelni baholang
- DeepSHAP yordamida modelni talqin qiling
- Yaxshiroq ma'lumot to'plash orqali modelni to'g'rilang
- Tasvirni kengaytirish modelni qanday yaxshilashi mumkinligini muhokama qiling
Yo'l davomida biz Python kodining ba'zi asosiy qismlarini muhokama qilamiz. To'liq loyihani "GitHub" da topishingiz mumkin.
Agar siz SHAP dasturida yangi boʻlsangiz, u holda videoniquyidagitomosha qiling. Agar ko'proq narsani xohlasangiz, mening SHAP kursimni ko'ring. Agar siz mening Axborotnomamga ro'yxatdan o'tsangiz, bepul kirishingiz mumkin :)
Python paketlari
# Imports import numpy as np import pandas as pd import matplotlib.pyplot as plt import glob import random from PIL import Image import cv2 import torch import torchvision from torchvision import transforms from torch.utils.data import DataLoader import shap from sklearn.metrics import mean_squared_error
Ma'lumotlar to'plami
Biz loyihani faqat bitta xonada ma'lumotlarni to'plash orqali boshlaymiz (bu bizni ta'qib qilish uchun qaytib keladi). Yuqorida aytib o'tilganidek, biz avtomatlashtirilgan mashinani quvvatlantirish uchun tasvirlardan foydalanamiz. Buning misollarini "Kaggle" da topishingiz mumkin. Bu rasmlarning barchasi 224 x 224 piksel.
Biz ulardan birini quyidagi kod bilan ko'rsatamiz. Rasm nomiga e'tibor bering (2-qator). Birinchi ikkita raqam 224 x 224 kvadrat ichida x va y koordinatalari. 1-rasmda siz yashil doira (8-qator) yordamida ushbu koordinatalarni ko'rsatganimizni ko'rishingiz mumkin.
#Load example image name = "32_50_c78164b4-40d2-11ed-a47b-a46bb6070c92.jpg" x = int(name.split("_")[0]) y = int(name.split("_")[1]) img = Image.open("../data/room_1/" + name) img = np.array(img) cv2.circle(img, (x, y), 8, (0, 255, 0), 3) plt.imshow(img)
Ushbu koordinatalar maqsadli o'zgaruvchidir. Model ularni tasvirdan kirish sifatida ishlatib, bashorat qiladi. Keyinchalik bu bashorat avtomobilni yo'naltirish uchun ishlatiladi. Bunday holda, siz mashinaning chap burilishgacha kelayotganini ko'rishingiz mumkin. Ideal yo'nalish yashil doira tomonidan berilgan koordinatalar tomon borishdir.
PyTorch modelini o'rgatish
Men SHAPga e'tibor qaratmoqchiman, shuning uchun biz modellashtirish kodiga juda chuqur kirmaymiz. Agar sizda biron bir savol bo'lsa, ularni sharhlarda so'rang.
Biz ImageDataset sinfini yaratishdan boshlaymiz. Bu bizning rasm ma'lumotlarimiz va maqsadli o'zgaruvchilarni yuklash uchun ishlatiladi. Buni bizning rasmlarimizgayo'llar yordamida amalga oshiradi. Shuni ta'kidlash kerakki, maqsadli o'zgaruvchilar qanday miqyosda - x va y ham -1 va1 orasida bo'ladi. kuchli>.
class ImageDataset(torch.utils.data.Dataset): def __init__(self, paths, transform): self.transform = transform self.paths = paths def __getitem__(self, idx): """Get image and target (x, y) coordinates""" # Read image path = self.paths[idx] image = cv2.imread(path, cv2.IMREAD_COLOR) image = Image.fromarray(image) # Transform image image = self.transform(image) # Get target target = self.get_target(path) target = torch.Tensor(target) return image, target def get_target(self,path): """Get the target (x, y) coordinates from path""" name = os.path.basename(path) items = name.split('_') x = items[0] y = items[1] # Scale between -1 and 1 x = 2.0 * (int(x)/ 224 - 0.5) # -1 left, +1 right y = 2.0 * (int(y) / 244 -0.5)# -1 top, +1 bottom return [x, y] def __len__(self): return len(self.paths)
Aslida, model o'rnatilganda avtomobilni boshqarish uchun faqat x bashoratlari qo'llaniladi. Masshtablash tufayli, x bashoratining belgisi avtomobil yo'nalishini aniqlaydi. Qachonx ‹ 0, mashina chapga burilishi kerak. Xuddi shunday,x › 0 bo‘lganda mashina o‘ngga burilishi kerak. X qiymati qanchalik katta bo'lsa, burilish shunchalik keskin bo'ladi.
Biz ImageDataset sinfidan o'qitish va tekshirish ma'lumotlar yuklagichlarini yaratish uchun foydalanamiz. Bu 1-xonadagi barcha tasvir yoʻllarini tasodifiy 80/20ga boʻlish orqali amalga oshiriladi. Oxir-oqibat, bizda 1,217va 305 bor. mos ravishda trening va tasdiqlash to'plamidagi tasvirlar.
TRANSFORMS = transforms.Compose([ transforms.ColorJitter(0.2, 0.2, 0.2, 0.2), transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) paths = glob.glob('../data/room_1/*') # Shuffle the paths random.shuffle(paths) # Create a datasets for training and validation split = int(0.8 * len(paths)) train_data = ImageDataset(paths[:split], TRANSFORMS) valid_data = ImageDataset(paths[split:], TRANSFORMS) # Prepare data for Pytorch model train_loader = DataLoader(train_data, batch_size=32, shuffle=True) valid_loader = DataLoader(valid_data, batch_size=valid_data.__len__())
valid_loaderning batch_sizega e'tibor bering. Biz tekshirish ma'lumotlar to'plamining uzunligidan foydalanmoqdamiz (masalan, 305). Bu bizga barcha tekshirish ma'lumotlarini bir iteratsiyada yuklash imkonini beradi. Agar siz kattaroq ma'lumotlar to'plamlari bilan ishlayotgan bo'lsangiz, kichikroq partiya hajmidan foydalanishingiz kerak bo'lishi mumkin.
Biz oldindan tayyorlangan ResNet18 modelini yuklaymiz (5-qator). model.fcni sozlash orqali biz oxirgi qatlamni yangilaymiz (6-qator). Bu 512 tugundan 2 ta maqsadli o'zgaruvchan tugungacha to'liq bog'langan qatlamdir. Ushbu modelni sozlash uchun biz Adam optimallashtiruvchisidan foydalanamiz (9-qator).
output_dim = 2 # x, y device = torch.device('mps') # or 'cuda' if you have a GPU # RESNET 18 model = torchvision.models.resnet18(pretrained=True) model.fc = torch.nn.Linear(512, output_dim) model = model.to(device) optimizer = torch.optim.Adam(model.parameters())
Men modelni GPU yordamida o'rgatganman (2-qator). Siz hali ham kodni protsessorda ishlatishingiz mumkin. Noldan boshlab o'qitish kabi nozik sozlash hisoblash qimmat emas!
Nihoyat, bizda namunaviy trening kodimiz bor. Biz yo'qotish funksiyasi sifatida MSE dan foydalangan holda 10 davr davomida mashq qilamiz. Bizning yakuniy modelimiz tekshirish to'plamida eng past MSEga ega bo'lgan modeldir.
name = "direction_model_1" # Change this to save a new model # Train the model min_loss = np.inf for epoch in range(10): model = model.train() for images, target in iter(train_loader): images = images.to(device) target = target.to(device) # Zero gradients of parameters optimizer.zero_grad() # Execute model to get outputs output = model(images) # Calculate loss loss = torch.nn.functional.mse_loss(output, target) # Run backpropogation to accumulate gradients loss.backward() # Update model parameters optimizer.step() # Calculate validation loss model = model.eval() images, target = next(iter(valid_loader)) images = images.to(device) target = target.to(device) output = model(images) valid_loss = torch.nn.functional.mse_loss(output, target) print("Epoch: {}, Validation Loss: {}".format(epoch, valid_loss.item())) if valid_loss < min_loss: print("Saving model") torch.save(model, '../models/{}.pth'.format(name)) min_loss = valid_loss
Baholash ko'rsatkichlari
Shu nuqtada biz modelimiz qanday ishlashini tushunmoqchimiz. Biz MSE ga qaraymiz va haqiqiy va bashorat qilingan x qiymatlarining tarqalish sxemalarini ko'rib chiqamiz. Biz hozircha y ga e'tibor bermaymiz, chunki u avtomobil yo'nalishiga ta'sir qilmaydi.
Trening va tasdiqlash to'plami
2-rasmda bu koʻrsatkichlar oʻqitish va tekshirish toʻplamida berilgan. Diagonal qizil chiziq mukammal bashoratlarni beradi. x ‹ 0 va x › 0 uchun bu chiziq atrofida o'xshash o'zgarishlar mavjud. Boshqacha qilib aytganda, model xuddi shunday aniqlik bilan chap va o'ng burilishlarni bashorat qila oladi. O'qitish va tekshirish to'plamidagi shunga o'xshash ishlash, shuningdek, modelning ortiqcha jihozlanmaganligini ko'rsatadi.
Yuqoridagi syujetni yaratish uchun biz model_baholash funksiyasidan foydalanamiz. E'tibor bering, ma'lumotlar yuklagichlari birinchi iteratsiyada barcha ma'lumotlarni yuklashi uchun yaratilishi kerak.
def model_evaluation(loaders,labels,save_path = None): """Evaluate direction models with mse and scatter plots loaders: list of data loaders labels: list of labels for plot title""" n = len(loaders) fig, axs = plt.subplots(1, n, figsize=(7*n, 6)) # Evalution metrics for i, loader in enumerate(loaders): # Load all data images, target = next(iter(loader)) images = images.to(device) target = target.to(device) output=model(images) # Get x predictions x_pred=output.detach().cpu().numpy()[:,0] x_target=target.cpu().numpy()[:,0] # Calculate MSE mse = mean_squared_error(x_target, x_pred) # Plot predcitons axs[i].scatter(x_target,x_pred) axs[i].plot([-1, 1], [-1, 1], color='r', linestyle='-', linewidth=2) axs[i].set_ylabel('Predicted x', size =15) axs[i].set_xlabel('Actual x', size =15) axs[i].set_title("{0} MSE: {1:.4f}".format(labels[i], mse),size = 18) if save_path != None: fig.savefig(save_path)
Quyidagi funktsiyadan foydalanganda nimani nazarda tutayotganimizni ko'rishingiz mumkin. Biz yangi train_loaderni yaratdik, bu toʻplam hajmini oʻquv maʼlumotlar toʻplamining uzunligiga moslashtirdi. Saqlangan modelni yuklash ham muhim (2-qator). Aks holda, siz oxirgi davrda o'qitilgan modeldan foydalanasiz.
# Load saved model model = torch.load('../models/direction_model_1.pth') model.eval() model.to(device) # Create new loader for all data train_loader = DataLoader(train_data, batch_size=train_data.__len__()) # Evaluate model on training and validation set loaders = [train_loader,valid_loader] labels = ["Train","Validation"] # Evaluate on training and validation set model_evaluation(loaders,labels)
Yangi joylarga ko'chish
Natijalar yaxshi ko'rinadi! Biz mashinaning yaxshi ishlashini kutgan edik va shunday bo'ldi. Biz uni yangi joyga ko'chirmagunimizcha:
Biz yangi joylardan ba'zi ma'lumotlarni yig'amiz (2-xona va 3-xona). Ushbu tasvirlar bo'yicha baholashni amalga oshirsangiz, bizning modelimiz unchalik yaxshi ishlamasligini ko'rishingiz mumkin. Bu g'alati! Mashina aynan bir yo'lda, shuning uchun xona nima uchun muhim?
SHAP yordamida modelni disk raskadrovka qilish
Javobni SHAPdan qidiramiz. Bu ma'lum bir bashorat uchun qaysi piksellar muhimligini tushunish uchun ishlatilishi mumkin. Saqlangan modelimizni yuklashdan boshlaymiz (2-qator). SHAP GPU uchun amalga oshirilmagan, shuning uchun biz qurilmani protsessorga o'rnatdik (5-6 qatorlar).
# Load saved model model = torch.load('../models/direction_model_1.pth') # Use CPU device = torch.device('cpu') model = model.to(device)
SHAP qiymatlarini hisoblash uchun biz fon rasmlarini olishimiz kerak. Qiymatlarni hisoblashda SHAP ushbu tasvirlar ustida birlashadi. Biz 100 ta rasmdan iborat to'plam_o'lchamidan foydalanmoqdamiz. Bu bizga oqilona taxminlarni berishi kerak. Rasmlar sonini ko'paytirish yaqinlashishni yaxshilaydi, lekin hisoblash vaqtini ham oshiradi.
#Load 100 images for background shap_loader = DataLoader(train_data, batch_size=100, shuffle=True) background, _ = next(iter(shap_loader)) background = background.to(device)
Biz modelimiz va fon rasmlariniDeepExplainerfunktsiyasiga o'tkazish orqali tushuntirish ob'ektini yaratamiz. Bu funksiya neyron tarmoqlar uchun SHAP qiymatlarini samarali tarzda yaqinlashtiradi. Muqobil sifatida uni GradientExplainer funksiyasi bilan almashtirishingiz mumkin.
#Create SHAP explainer explainer = shap.DeepExplainer(model, background)
Biz ikkita misol tasvirini yuklaymiz - o'ngga va chapga burilish (2-qator) va ularni o'zgartiramiz (6-qator). Bu juda muhim, chunki tasvirlar modelni o'rgatish uchun ishlatiladigan formatda bo'lishi kerak. Keyin ushbu tasvirlar yordamida qilingan bashoratlar uchun SHAP qiymatlarini hisoblaymiz (10-qator).
# Load test images of right and left turn paths = glob.glob('../data/room_1/*') test_images = [Image.open(paths[0]), Image.open(paths[3])] test_images = np.array(test_images) test_input = [TRANSFORMS(img) for img in test_images] test_input = torch.stack(test_input).to(device) # Get SHAP values shap_values = explainer.shap_values(test_input)
Nihoyat, biz image_plot funksiyasi yordamida SHAP qiymatlarini ko'rsatishimiz mumkin. Biroq, biz birinchi navbatda ularni qayta qurishimiz kerak. SHAP qiymatlari o'lchamlar bilan qaytariladi:
( #maqsadlar, #tasvirlar, #kanallar, #kenglik, #balandlik)
Biz transpose funktsiyasidan foydalanamiz, shuning uchun bizda o'lchamlar mavjud:
( #maqsadlar, #tasvirlar, #kenglik, #balandlik, #kanallar)
Esda tuting, biz asl rasmlarni image_plot funksiyasiga o‘tkazdik. test_input tasvirlari oʻzgarishlar tufayli gʻalati koʻrinadi.
# Reshape shap values and images for plotting shap_numpy = list(np.array(shap_values).transpose(0,1,3,4,2)) test_numpy = np.array([np.array(img) for img in test_images]) shap.image_plot(shap_numpy, test_numpy,show=False)
Natijani 4-rasmda ko'rishingiz mumkin. Birinchi ustun asl tasvirlarni beradi. Ikkinchi va uchinchi ustunlar mos ravishda x va y bashorati uchun SHAP qiymatlari. Moviy piksellar bashoratni pasaytirdi. Taqqoslash uchun, qizil piksellar bashoratni oshirdi. Boshqacha qilib aytadigan bo'lsak, x prognozi uchun qizil piksellar o'ngga keskinroq burilishga olib keldi.
Endi biz bir joyga boryapmiz. Muhim natija shundaki, model fon piksellaridan foydalanmoqda. Buni 5-rasmda ko'rishingiz mumkin, bu erda biz o'ng burilish uchun x prognozini kattalashtiramiz. Boshqacha qilib aytganda, fon bashorat qilish uchun muhimdir. Bu yomon ishlashni tushuntiradi! Biz yangi xonaga ko'chganimizda, fon o'zgardi va bizning bashoratlarimiz ishonchsiz bo'lib qoldi.
Model 1-xonadagi ma'lumotlarga haddan tashqari moslashtirilgan. Har bir tasvirda bir xil ob'ektlar va fon mavjud. Natijada, model ularni chap va o'ng burilishlar bilan bog'laydi. Biz buni baholashda aniqlay olmadik, chunki biz o'qitish va tasdiqlash tasvirlarida bir xil ma'lumotga egamiz.
Modelni takomillashtirish
Biz modelimiz barcha sharoitlarda yaxshi ishlashini istaymiz. Bunga erishish uchun biz faqat trekdagi piksellardan foydalanishini kutamiz. Shunday qilib, keling, modelni yanada mustahkam qilishning ba'zi usullarini muhokama qilaylik.
Yangi ma'lumotlarni yig'ish
Eng yaxshi yechim shunchaki ko'proq ma'lumot to'plashdir. Bizda allaqachon 2 va 3 xonalardan ba'zilari bor. Xuddi shu jarayondan so'ng biz barcha 3 xonadagi ma'lumotlardan foydalangan holda yangi modelni o'rgatamiz. 7-rasmga nazar tashlasak, endi u yangi xonalardagi tasvirlarda yaxshiroq ishlashga ega.
Umid qilamanki, bir nechta xonalardan olingan ma'lumotlarga o'rgatish orqali biz burilishlar va fon o'rtasidagi bog'lanishni buzamiz. Chap va o'ng burilishlarda turli xil narsalar mavjud, ammo trek bir xil bo'lib qoladi. Model trekni bashorat qilish uchun muhim ekanligini bilishi kerak.
Buni yangi model uchun SHAP qiymatlariga qarab tasdiqlashimiz mumkin. Bu biz4-rasmda ko‘rgan bir xil burilishlar uchun. Endi fon piksellariga kamroq og'irlik qo'yildi. OK, bu mukammal emas, lekin biz bir joyga boryapmiz.
Biz ma'lumotlarni to'plashni davom ettirishimiz mumkin. Qanchalik koʻp maʼlumotlarni toʻplasak, modelimiz shunchalik mustahkam boʻladi. Biroq, ma'lumotlarni yig'ish ko'p vaqt talab qilishi mumkin (va zerikarli!). Buning o'rniga, biz ma'lumotlarni ko'paytirishga qarashimiz mumkin.
Ma'lumotlarni ko'paytirish
Ma'lumotlarni ko'paytirish - bu kod yordamida tasvirlarni muntazam yoki tasodifiy o'zgartirishimiz. Bu bizga shovqinni sun'iy ravishda kiritish va ma'lumotlar to'plamimiz hajmini oshirish imkonini beradi.
Misol uchun, vertikal o'qdatasvirlarni aylantirish orqali ma'lumotlar to'plamimiz hajmini ikki baravar oshirishimiz mumkin. Biz buni qila olamiz, chunki bizning trekimiz nosimmetrikdir. 9-rasmda ko'rsatilganidek, o'chirish ham foydali usul bo'lishi mumkin. Bu ob'ektlar yoki butun fon olib tashlangan rasmlarni o'z ichiga oladi.
Kuchli modelni yaratishda siz yorug'lik sharoitlari va tasvir sifati kabi omillarni ham hisobga olishingiz kerak. Biz ularni rang jitter yordamida yoki shovqin qo'shish orqali taqlid qilishimiz mumkin. Agar siz ushbu usullarning barchasi haqida bilishni istasangiz, quyidagi maqolani ko'ring.
«Chuqur oʻrganish uchun rasmlarni kengaytirish
Python yordamida maʼlumotlarni aylantirish, yorqinlikni, rang jitterini va tasodifiy shovqinni sozlash orqali koʻpaytirishtowardsdatascience.com”
Yuqoridagi maqolada, shuningdek, nega bu kengaytmalar modelni yanada mustahkam qilganligini aytish qiyinligini muhokama qilamiz. Biz modelni ko'plab muhitlarda o'rnatishimiz mumkin, ammo bu ko'p vaqt talab etadi. Yaxshiyamki, SHAP muqobil sifatida ishlatilishi mumkin. Ma'lumotlar to'plashda bo'lgani kabi, bu bizga modelning bashorat qilish usulini kengaytirishlar qanday o'zgartirganligi haqida tushuncha berishi mumkin.
Umid qilamanki, sizga ushbu maqola yoqdi! Siz meningyo'naltirilgan a'zolarim :)ga aylanish orqali meni qo'llab-quvvatlashingiz mumkin
| Twitter | "YouTube" | Axborotnoma — Python SHAP kursiga BEPUL kirish uchun ro‘yxatdan o‘ting
Ma'lumotlar to'plami
JatRacer rasmlari (CC0: Public Domain) https://www.kaggle.com/datasets/conorsully1/jatracer-images
Ma'lumotnomalar
SHAP, PyTorch Deep Explainer MNIST misolihttps://shap.readthedocs.io/en/latest/example_notebooks/image_examples/image_classification/PyTorch%20Deep%20Explainer%20MNIST.%20ex