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" | AxborotnomaPython 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