หน้านี้ได้รับการแปลโดย Cloud Translation API
Switch to English

การไล่ระดับสีแบบบูรณาการ

ดูใน TensorFlow.org เรียกใช้ใน Google Colab ดูบน GitHub ดาวน์โหลดสมุดบันทึก ดูโมเดล TF Hub

บทช่วยสอนนี้แสดงให้เห็นถึงวิธีการใช้ Integrated Gradients (IG) ซึ่งเป็นเทคนิค AI ที่ อธิบายได้ซึ่งนำมาใช้ในเอกสาร Axiomatic Attribution สำหรับ Deep Networks IG มีวัตถุประสงค์เพื่ออธิบายความสัมพันธ์ระหว่างการคาดการณ์ของโมเดลในแง่ของคุณสมบัติ มีกรณีการใช้งานมากมายรวมถึงการทำความเข้าใจการนำเข้าคุณลักษณะการระบุการบิดเบือนข้อมูลและการแก้ไขข้อบกพร่องประสิทธิภาพของโมเดล

IG กลายเป็นเทคนิคการตีความที่ได้รับความนิยมเนื่องจากสามารถนำไปใช้กับแบบจำลองที่แตกต่างกันได้ (เช่นรูปภาพข้อความข้อมูลที่มีโครงสร้าง) ความสะดวกในการนำไปใช้เหตุผลเชิงทฤษฎีและประสิทธิภาพในการคำนวณเมื่อเทียบกับแนวทางอื่นที่ช่วยให้สามารถปรับขนาดไปยังเครือข่ายและคุณลักษณะขนาดใหญ่ ช่องว่างเช่นรูปภาพ

ในบทช่วยสอนนี้คุณจะอธิบายถึงการใช้งาน IG ทีละขั้นตอนเพื่อทำความเข้าใจการนำเข้าคุณลักษณะพิกเซลของตัวจำแนกรูปภาพ ตัวอย่างเช่นพิจารณา ภาพ เรือดับเพลิงที่พ่นน้ำ คุณจะจัดประเภทภาพนี้เป็นเรือไฟและอาจเน้นพิกเซลที่ประกอบขึ้นเป็นเรือและปืนใหญ่น้ำว่ามีความสำคัญต่อการตัดสินใจของคุณ โมเดลของคุณจะจัดประเภทภาพนี้เป็นเรือไฟในบทช่วยสอนนี้ด้วย อย่างไรก็ตามมันเน้นพิกเซลเดียวกันกับความสำคัญเมื่ออธิบายการตัดสินใจหรือไม่?

ในภาพด้านล่างที่มีชื่อว่า "IG Attribution Mask" และ "Original + IG Mask Overlay" คุณจะเห็นว่าแบบจำลองของคุณจะเน้น (เป็นสีม่วง) พิกเซลที่ประกอบไปด้วยปืนฉีดน้ำของเรือและหัวฉีดน้ำว่ามีความสำคัญมากกว่าตัวเรือ การตัดสินใจของมัน โมเดลของคุณจะอธิบายถึงเรือดับเพลิงใหม่อย่างไร แล้วเรือดับเพลิงที่ไม่มีหัวฉีดน้ำล่ะ? อ่านต่อเพื่อเรียนรู้เพิ่มเติมเกี่ยวกับการทำงานของ IG และวิธีใช้ IG กับโมเดลของคุณเพื่อทำความเข้าใจความสัมพันธ์ระหว่างการคาดการณ์และคุณสมบัติพื้นฐาน

ภาพที่ส่งออก 1

ติดตั้ง

import matplotlib.pylab as plt
import numpy as np
import tensorflow as tf
import tensorflow_hub as hub

ดาวน์โหลดตัวจำแนกรูปภาพที่กำหนดไว้ล่วงหน้าจาก TF-Hub

IG สามารถนำไปใช้กับโมเดลที่แตกต่างกันได้ ด้วยจิตวิญญาณของกระดาษต้นฉบับคุณจะใช้ Inception V1 รุ่นที่ผ่านการฝึกอบรมล่วงหน้าซึ่งคุณจะดาวน์โหลดจาก TensorFlow Hub

model = tf.keras.Sequential([
    hub.KerasLayer(
        name='inception_v1',
        handle='https://tfhub.dev/google/imagenet/inception_v1/classification/4',
        trainable=False),
])
model.build([None, 224, 224, 3])
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
inception_v1 (KerasLayer)    (None, 1001)              6633209   
=================================================================
Total params: 6,633,209
Trainable params: 0
Non-trainable params: 6,633,209
_________________________________________________________________

จากหน้าโมดูลคุณต้องคำนึงถึงสิ่งต่อไปนี้เกี่ยวกับ Inception V1:

อินพุต : รูปร่างอินพุตที่คาดไว้สำหรับโมเดลคือ (None, 224, 224, 3) นี่คือเทนเซอร์ 4 มิติที่หนาแน่นของ dtype float32 และรูปร่าง (batch_size, height, width, RGB channels) ซึ่งองค์ประกอบคือค่าสี RGB ของพิกเซลที่ปรับให้เป็นมาตรฐานในช่วง [0, 1] องค์ประกอบแรกคือ None เพื่อระบุว่าโมเดลสามารถใช้ขนาดแบตช์จำนวนเต็มเท่าใดก็ได้

เอาท์พุต : A tf.Tensor ของล็อกในรูปแบบของ (batch_size, 1001) แต่ละแถวแสดงคะแนนที่คาดการณ์ของโมเดลสำหรับแต่ละคลาส 1,001 จาก ImageNet สำหรับดัชนีคลาสที่คาดการณ์สูงสุดของโมเดลคุณสามารถใช้ tf.argmax(predictions, axis=-1) นอกจากนี้คุณยังสามารถแปลง logit output ของโมเดลเป็นค่าความน่าจะเป็นที่คาดการณ์ไว้ในทุกคลาสโดยใช้ tf.nn.softmax(predictions, axis=-1) เพื่อหาจำนวนความไม่แน่นอนของแบบจำลองรวมทั้งสำรวจคลาสที่คาดการณ์ที่คล้ายกันสำหรับการดีบัก

def load_imagenet_labels(file_path):
  labels_file = tf.keras.utils.get_file('ImageNetLabels.txt', file_path)
  with open(labels_file) as reader:
    f = reader.read()
    labels = f.splitlines()
  return np.array(labels)
imagenet_labels = load_imagenet_labels('https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt')

โหลดและประมวลผลภาพล่วงหน้าด้วย tf.image

คุณจะแสดงภาพ IG โดยใช้ภาพสองภาพจาก Wikimedia Commons ได้แก่ Fireboat และ Giant Panda

def read_image(file_name):
  image = tf.io.read_file(file_name)
  image = tf.image.decode_jpeg(image, channels=3)
  image = tf.image.convert_image_dtype(image, tf.float32)
  image = tf.image.resize_with_pad(image, target_height=224, target_width=224)
  return image
img_url = {
    'Fireboat': 'http://storage.googleapis.com/download.tensorflow.org/example_images/San_Francisco_fireboat_showing_off.jpg',
    'Giant Panda': 'http://storage.googleapis.com/download.tensorflow.org/example_images/Giant_Panda_2.jpeg',
}

img_paths = {name: tf.keras.utils.get_file(name, url) for (name, url) in img_url.items()}
img_name_tensors = {name: read_image(img_path) for (name, img_path) in img_paths.items()}
Downloading data from http://storage.googleapis.com/download.tensorflow.org/example_images/San_Francisco_fireboat_showing_off.jpg
3956736/3954129 [==============================] - 0s 0us/step
Downloading data from http://storage.googleapis.com/download.tensorflow.org/example_images/Giant_Panda_2.jpeg
811008/802859 [==============================] - 0s 0us/step

plt.figure(figsize=(8, 8))
for n, (name, img_tensors) in enumerate(img_name_tensors.items()):
  ax = plt.subplot(1, 2, n+1)
  ax.imshow(img_tensors)
  ax.set_title(name)
  ax.axis('off')
plt.tight_layout()

png

จำแนกภาพ

เริ่มต้นด้วยการจำแนกรูปภาพเหล่านี้และแสดงการคาดการณ์ที่มั่นใจที่สุด 3 อันดับแรก ต่อไปนี้เป็นฟังก์ชั่นยูทิลิตี้เพื่อดึงป้ายกำกับและความน่าจะเป็นที่คาดคะเน k อันดับต้น ๆ

def top_k_predictions(img, k=3):
  image_batch = tf.expand_dims(img, 0)
  predictions = model(image_batch)
  probs = tf.nn.softmax(predictions, axis=-1)
  top_probs, top_idxs = tf.math.top_k(input=probs, k=k)
  top_labels = imagenet_labels[tuple(top_idxs)]
  return top_labels, top_probs[0]
for (name, img_tensor) in img_name_tensors.items():
  plt.imshow(img_tensor)
  plt.title(name, fontweight='bold')
  plt.axis('off')
  plt.show()

  pred_label, pred_prob = top_k_predictions(img_tensor)
  for label, prob in zip(pred_label, pred_prob):
    print(f'{label}: {prob:0.1%}')

png

fireboat: 32.6%
pier: 12.7%
suspension bridge: 5.7%

png

giant panda: 89.4%
teddy: 0.3%
gibbon: 0.3%

คำนวณการไล่ระดับสีแบบรวม

แบบจำลองของคุณ Inception V1 เป็นฟังก์ชันที่เรียนรู้ซึ่งอธิบายการแมประหว่างพื้นที่คุณลักษณะอินพุตของคุณค่าพิกเซลภาพและพื้นที่เอาต์พุตที่กำหนดโดยค่าความน่าจะเป็นของคลาส ImageNet ระหว่าง 0 ถึง 1 วิธีการตีความในช่วงต้นสำหรับเครือข่ายประสาทที่กำหนดคะแนนความสำคัญของคุณลักษณะโดยใช้ การไล่ระดับสีซึ่งจะบอกให้คุณทราบว่าพิกเซลใดมีความชันมากที่สุดเมื่อเทียบกับการคาดคะเนของโมเดลของคุณ ณ จุดที่กำหนดตามฟังก์ชันการคาดคะเนของโมเดลของคุณ อย่างไรก็ตามการไล่ระดับสีเพียงอธิบายการเปลี่ยนแปลงในท้องถิ่นในฟังก์ชั่นการทำนายรูปแบบของคุณด้วยความเคารพต่อค่าพิกเซลและไม่เต็มที่อธิบายฟังก์ชันการทำนายรูปแบบทั้งหมดของคุณ ในขณะที่โมเดลของคุณ "เรียนรู้" ความสัมพันธ์ระหว่างช่วงของแต่ละพิกเซลกับคลาส ImageNet ที่ถูกต้องอย่างสมบูรณ์การไล่ระดับสีของพิกเซลนี้จะ อิ่มตัว ซึ่งหมายถึงมีขนาดเล็กมากขึ้นเรื่อย ๆ และถึงศูนย์ พิจารณาฟังก์ชันโมเดลอย่างง่ายด้านล่าง:

def f(x):
  """A simplified model function."""
  return tf.where(x < 0.8, x, 0.8)

def interpolated_path(x):
  """A straight line path."""
  return tf.zeros_like(x)

x = tf.linspace(start=0.0, stop=1.0, num=6)
y = f(x)
fig = plt.figure(figsize=(12, 5))
ax0 = fig.add_subplot(121)
ax0.plot(x, f(x), marker='o')
ax0.set_title('Gradients saturate over F(x)', fontweight='bold')
ax0.text(0.2, 0.5, 'Gradients > 0 = \n x is important')
ax0.text(0.7, 0.85, 'Gradients = 0 \n x not important')
ax0.set_yticks(tf.range(0, 1.5, 0.5))
ax0.set_xticks(tf.range(0, 1.5, 0.5))
ax0.set_ylabel('F(x) - model true class predicted probability')
ax0.set_xlabel('x - (pixel value)')

ax1 = fig.add_subplot(122)
ax1.plot(x, f(x), marker='o')
ax1.plot(x, interpolated_path(x), marker='>')
ax1.set_title('IG intuition', fontweight='bold')
ax1.text(0.25, 0.1, 'Accumulate gradients along path')
ax1.set_ylabel('F(x) - model true class predicted probability')
ax1.set_xlabel('x - (pixel value)')
ax1.set_yticks(tf.range(0, 1.5, 0.5))
ax1.set_xticks(tf.range(0, 1.5, 0.5))
ax1.annotate('Baseline', xy=(0.0, 0.0), xytext=(0.0, 0.2),
             arrowprops=dict(facecolor='black', shrink=0.1))
ax1.annotate('Input', xy=(1.0, 0.0), xytext=(0.95, 0.2),
             arrowprops=dict(facecolor='black', shrink=0.1))
plt.show();

png

  • ซ้าย : การไล่ระดับสีของโมเดลสำหรับพิกเซล x เป็นค่าบวกระหว่าง 0.0 ถึง 0.8 แต่ไปที่ 0.0 ระหว่าง 0.8 ถึง 1.0 Pixel x มีผลอย่างชัดเจนในการผลักดันโมเดลของคุณไปสู่ความน่าจะเป็นที่คาดการณ์ไว้ 80% ในคลาสจริง มันสมเหตุสมผลไหมที่ความสำคัญของพิกเซล x เล็กหรือไม่ต่อเนื่อง?

  • ถูกต้อง : สัญชาตญาณเบื้องหลัง IG คือการสะสมการไล่ระดับสีในพื้นที่ของพิกเซล x และระบุความสำคัญของมันเป็นคะแนนสำหรับจำนวนที่เพิ่มหรือลบให้กับความน่าจะเป็นของคลาสเอาต์พุตโดยรวมของโมเดลของคุณ คุณสามารถแยกย่อยและคำนวณ IG ได้ 3 ส่วน:

    1. สอดแทรกขั้นตอนเล็ก ๆ ตามเส้นตรงในช่องว่างระหว่าง 0 (เส้นฐานหรือจุดเริ่มต้น) และ 1 (ค่าพิกเซลอินพุต)
    2. คำนวณการไล่ระดับสีในแต่ละขั้นตอนระหว่างการคาดคะเนของแบบจำลองของคุณตามแต่ละขั้นตอน
    3. ประมาณค่าอินทิกรัลระหว่างพื้นฐานและอินพุตของคุณโดยการสะสม (ค่าเฉลี่ยสะสม) การไล่ระดับในท้องถิ่นเหล่านี้

เพื่อเสริมสร้างสัญชาตญาณนี้คุณจะได้เรียนรู้ทั้ง 3 ส่วนนี้โดยใช้ IG กับตัวอย่างภาพ "เรือไฟ" ด้านล่าง

สร้างพื้นฐาน

พื้นฐานคือภาพอินพุตที่ใช้เป็นจุดเริ่มต้นในการคำนวณความสำคัญของคุณลักษณะ โดยสังหรณ์ใจคุณสามารถนึกถึงบทบาทในการอธิบายพื้นฐานในการแสดงผลกระทบของการไม่มีพิกเซลแต่ละพิกเซลในการทำนาย "เรือดับเพลิง" เพื่อเปรียบเทียบกับผลกระทบของแต่ละพิกเซลที่มีต่อการคาดคะเน "เรือดับเพลิง" เมื่ออยู่ในภาพอินพุต ด้วยเหตุนี้การเลือกเส้นฐานจึงมีบทบาทสำคัญในการตีความและการแสดงภาพการนำเข้าคุณลักษณะพิกเซล สำหรับการอภิปรายเพิ่มเติมเกี่ยวกับการเลือกพื้นฐานโปรดดูแหล่งข้อมูลในส่วน "ขั้นตอนถัดไป" ที่ด้านล่างของบทช่วยสอนนี้ ที่นี่คุณจะใช้ภาพสีดำที่มีค่าพิกเซลเป็นศูนย์ทั้งหมด

ตัวเลือกอื่น ๆ ที่คุณสามารถทดลอง ได้แก่ ภาพสีขาวทั้งหมดหรือภาพสุ่มซึ่งคุณสามารถสร้างด้วย tf.random.uniform(shape=(224,224,3), minval=0.0, maxval=1.0)

baseline = tf.zeros(shape=(224,224,3))
plt.imshow(baseline)
plt.title("Baseline")
plt.axis('off')
plt.show()

png

แกะสูตรเป็นรหัส

สูตรสำหรับการไล่ระดับสีแบบรวมมีดังนี้:

$ IntegratedGradients_ {i} (x) :: = (x_ {i} - x '_ {i}) \ times \ int _ {\ alpha = 0} ^ 1 \ frac {\ partial F (x' + \ alpha \ times (x - x '))} {\ partial x_i} {d \ alpha} $

ที่ไหน:

คุณลักษณะ $ _ {i} $ =
$ x $ = อินพุต
$ x '$ = พื้นฐาน
$ \ alpha $ = ค่าคงที่การแก้ไขต่อคุณลักษณะที่เกี่ยวข้องโดย

ในทางปฏิบัติการคำนวณอินทิกรัลที่แน่นอนไม่สามารถเป็นตัวเลขได้เสมอไปและอาจมีค่าใช้จ่ายในการคำนวณดังนั้นคุณจะคำนวณการประมาณเชิงตัวเลขต่อไปนี้:

$ IntegratedGrads ^ {ประมาณ} _ {i} (x) :: = (x_ {i} -x '_ {i}) \ times \ sum_ {k = 1} ^ {m} \ frac {\ partial F (x '+ \ frac {k} {m} \ times (x - x'))} {\ partial x_ {i}} \ times \ frac {1} {m} $

ที่ไหน:

$ _ {i} $ = คุณลักษณะ (แต่ละพิกเซล)
$ x $ = อินพุต (เทนเซอร์ภาพ)
$ x '$ = พื้นฐาน (เทนเซอร์ภาพ)
$ k $ = ค่าคงที่อัตราการรบกวนของคุณลักษณะที่ปรับขนาด
$ m $ = จำนวนขั้นตอนในการประมาณผลรวมของ Riemann ของอินทิกรัล
$ (x_ {i} -x '_ {i}) $ = คำศัพท์สำหรับความแตกต่างจากค่าพื้นฐาน สิ่งนี้จำเป็นในการปรับขนาดการไล่ระดับสีแบบรวมและคงไว้ในรูปแบบของภาพต้นฉบับ เส้นทางจากภาพพื้นฐานไปยังอินพุตอยู่ในพื้นที่พิกเซล เนื่องจาก IG คุณกำลังรวมเป็นเส้นตรง (การแปลงเชิงเส้น) สิ่งนี้จึงเทียบเท่ากับระยะอินทิกรัลของอนุพันธ์ของฟังก์ชันภาพที่ถูกสอดแทรกโดยเทียบกับ $ \ alpha $ โดยมีขั้นตอนเพียงพอ อินทิกรัลจะรวมการไล่ระดับสีของแต่ละพิกเซลคูณกับการเปลี่ยนแปลงของพิกเซลตามเส้นทาง ง่ายกว่าในการใช้การรวมนี้เป็นขั้นตอนเดียวกันจากภาพหนึ่งไปยังอีกภาพหนึ่งโดยแทนที่ $ x: = (x '+ \ alpha (xx')) $ ดังนั้นการเปลี่ยนแปลงของตัวแปรจะทำให้ $ dx = (xx ') d \ alpha $ เงื่อนไข $ (xx ') $ เป็นค่าคงที่และแยกตัวประกอบออกจากอินทิกรัล

สลับภาพ

$ IntegratedGrads ^ {ประมาณ} _ {i} (x) :: = (x_ {i} -x '_ {i}) \ times \ sum_ {k = 1} ^ {m} \ frac {\ partial F (\ overbrace {x '+ \ frac {k} {m} \ times (x - x')} ^ \ text {สอดแทรกรูปภาพ m ที่ช่วงเวลา k})} {\ partial x_ {i}} \ times \ frac {1} {m} $

ขั้นแรกคุณจะสร้างการ แก้ไขเชิงเส้น ระหว่างเส้นฐานและภาพต้นฉบับ คุณสามารถคิดว่าภาพที่ถูกแก้ไขเป็นขั้นตอนเล็ก ๆ ในช่องว่างระหว่างข้อมูลพื้นฐานและอินพุตของคุณซึ่งแสดงด้วย $ \ alpha $ ในสมการดั้งเดิม

m_steps=50
alphas = tf.linspace(start=0.0, stop=1.0, num=m_steps+1) # Generate m_steps intervals for integral_approximation() below.
def interpolate_images(baseline,
                       image,
                       alphas):
  alphas_x = alphas[:, tf.newaxis, tf.newaxis, tf.newaxis]
  baseline_x = tf.expand_dims(baseline, axis=0)
  input_x = tf.expand_dims(image, axis=0)
  delta = input_x - baseline_x
  images = baseline_x +  alphas_x * delta
  return images

ลองใช้ฟังก์ชันด้านบนเพื่อสร้างภาพที่ถูกสอดแทรกตามเส้นทางเชิงเส้นในช่วงเวลาอัลฟาระหว่างภาพพื้นฐานสีดำและภาพตัวอย่าง "Fireboat"

interpolated_images = interpolate_images(
    baseline=baseline,
    image=img_name_tensors['Fireboat'],
    alphas=alphas)

ลองนึกภาพภาพที่ถูกสอดแทรก หมายเหตุ: อีกวิธีหนึ่งในการคิดเกี่ยวกับค่าคงที่ $ \ alpha $ ก็คือการเพิ่มความเข้มของภาพที่ถูกแก้ไขแต่ละภาพ

fig = plt.figure(figsize=(20, 20))

i = 0
for alpha, image in zip(alphas[0::10], interpolated_images[0::10]):
  i += 1
  plt.subplot(1, len(alphas[0::10]), i)
  plt.title(f'alpha: {alpha:.1f}')
  plt.imshow(image)
  plt.axis('off')

plt.tight_layout();

png

คำนวณการไล่ระดับสี

ตอนนี้เรามาดูวิธีการคำนวณการไล่ระดับสีเพื่อวัดความสัมพันธ์ระหว่างการเปลี่ยนแปลงคุณลักษณะและการเปลี่ยนแปลงในการคาดคะเนของแบบจำลอง ในกรณีของรูปภาพการไล่ระดับสีจะบอกให้เราทราบว่าพิกเซลใดมีผลมากที่สุดต่อโมเดลที่คาดการณ์ความน่าจะเป็นของคลาส

$ IntegratedGrads ^ {ประมาณ} _ {i} (x) :: = (x_ {i} -x '_ {i}) \ times \ sum_ {k = 1} ^ {m} \ frac {\ overbrace {\ partial F (\ text {interpolated images})} ^ \ text {compute gradients}} {\ partial x_ {i}} \ times \ frac {1} {m} $

ที่ไหน:
$ F () $ = ฟังก์ชันการคาดคะเนของโมเดลของคุณ
$ \ frac {\ partial {F}} {\ partial {x_i}} $ = gradient (เวกเตอร์ของอนุพันธ์บางส่วน $ \ partial $) ของฟังก์ชันการคาดคะเนของโมเดล F เทียบกับคุณลักษณะแต่ละอย่าง $ x_i $

TensorFlow ทำให้การไล่ระดับสีของคอมพิวเตอร์เป็นเรื่องง่ายสำหรับคุณด้วย tf.GradientTape

def compute_gradients(images, target_class_idx):
  with tf.GradientTape() as tape:
    tape.watch(images)
    logits = model(images)
    probs = tf.nn.softmax(logits, axis=-1)[:, target_class_idx]
  return tape.gradient(probs, images)

ลองคำนวณการไล่ระดับสีสำหรับแต่ละภาพตามเส้นทางการแก้ไขตามผลลัพธ์ที่ถูกต้อง จำได้ว่าแบบจำลองของคุณส่งคืน Tensor มีรูปร่าง (1, 1001) พร้อมกับบันทึกที่คุณแปลงเป็นความน่าจะเป็นที่คาดการณ์ไว้สำหรับแต่ละคลาส คุณต้องส่งดัชนีคลาสเป้าหมาย ImageNet ที่ถูกต้องไปยังฟังก์ชัน compute_gradients สำหรับรูปภาพของคุณ

path_gradients = compute_gradients(
    images=interpolated_images,
    target_class_idx=555)

สังเกตรูปร่างผลลัพธ์ของ (n_interpolated_images, img_height, img_width, RGB) ซึ่งทำให้เรามีการไล่ระดับสีสำหรับทุกพิกเซลของทุกภาพตามเส้นทางการแก้ไข คุณสามารถคิดว่าการไล่ระดับสีเหล่านี้เป็นการวัดการเปลี่ยนแปลงในการคาดการณ์ของแบบจำลองของคุณสำหรับแต่ละขั้นตอนเล็ก ๆ ในพื้นที่คุณลักษณะ

print(path_gradients.shape)
(51, 224, 224, 3)

แสดงภาพความอิ่มตัวของการไล่ระดับสี

โปรดจำไว้ว่าการไล่ระดับสีที่คุณเพิ่งคำนวณข้างต้นอธิบายการเปลี่ยนแปลง ในท้องถิ่น ของความน่าจะเป็น "เรือดับเพลิง" ที่คาดการณ์ไว้ของโมเดลของคุณและอาจทำให้ อิ่มตัว ได้

แนวคิดเหล่านี้แสดงให้เห็นโดยใช้การไล่ระดับสีที่คุณคำนวณไว้ข้างต้นใน 2 พล็อตด้านล่าง

pred = model(interpolated_images)
pred_proba = tf.nn.softmax(pred, axis=-1)[:, 555]

plt.figure(figsize=(10, 4))
ax1 = plt.subplot(1, 2, 1)
ax1.plot(alphas, pred_proba)
ax1.set_title('Target class predicted probability over alpha')
ax1.set_ylabel('model p(target class)')
ax1.set_xlabel('alpha')
ax1.set_ylim([0, 1])

ax2 = plt.subplot(1, 2, 2)
# Average across interpolation steps
average_grads = tf.reduce_mean(path_gradients, axis=[1, 2, 3])
# Normalize gradients to 0 to 1 scale. E.g. (x - min(x))/(max(x)-min(x))
average_grads_norm = (average_grads-tf.math.reduce_min(average_grads))/(tf.math.reduce_max(average_grads)-tf.reduce_min(average_grads))
ax2.plot(alphas, average_grads_norm)
ax2.set_title('Average pixel gradients (normalized) over alpha')
ax2.set_ylabel('Average pixel gradients')
ax2.set_xlabel('alpha')
ax2.set_ylim([0, 1]);
(0.0, 1.0)

png

  • ซ้าย : พล็อตนี้แสดงให้เห็นว่าความเชื่อมั่นของโมเดลของคุณในคลาส "Fireboat" นั้นแตกต่างกันไปอย่างไร สังเกตว่าการไล่ระดับสีหรือความลาดชันของเส้นส่วนใหญ่แบนหรืออิ่มตัวระหว่าง 0.6 ถึง 1.0 ก่อนที่จะตกลงที่ "Fireboat" สุดท้ายที่คาดการณ์ความน่าจะเป็นประมาณ 40%

  • ขวา : พล็อตด้านขวาจะแสดงขนาดของการไล่ระดับสีเฉลี่ยเหนืออัลฟาโดยตรงมากขึ้น สังเกตว่าค่าเข้าใกล้อย่างรวดเร็วและลดลงต่ำกว่าศูนย์ได้อย่างไร ในความเป็นจริงโมเดลของคุณ "เรียนรู้" มากที่สุดจากการไล่ระดับสีที่ค่าอัลฟาต่ำกว่าก่อนที่จะอิ่มตัว โดยสังหรณ์ใจคุณสามารถคิดได้ว่าสิ่งนี้เนื่องจากแบบจำลองของคุณได้เรียนรู้พิกเซลเช่นปืนฉีดน้ำเพื่อทำการทำนายที่ถูกต้องโดยส่งการไล่ระดับพิกเซลเหล่านี้ไปที่ศูนย์ แต่ก็ยังค่อนข้างไม่แน่นอนและมุ่งเน้นไปที่พิกเซลสะพานปลอมหรือจุดดำน้ำเนื่องจากค่าอัลฟาเข้าใกล้ ภาพอินพุตต้นฉบับ

เพื่อให้แน่ใจว่าพิกเซลปืนใหญ่น้ำที่สำคัญเหล่านี้สะท้อนให้เห็นว่ามีความสำคัญต่อการทำนาย "เรือดับเพลิง" คุณจะดำเนินการต่อด้านล่างเพื่อเรียนรู้วิธีสะสมการไล่ระดับสีเหล่านี้เพื่อประมาณว่าแต่ละพิกเซลส่งผลต่อความน่าจะเป็นที่คาดการณ์ไว้ใน "เรือดับเพลิง" อย่างถูกต้อง

สะสมการไล่ระดับสี (การประมาณเชิงปริพันธ์)

มีหลายวิธีที่คุณสามารถใช้เกี่ยวกับการคำนวณค่าประมาณเชิงตัวเลขของอินทิกรัลสำหรับ IG ที่มีการแลกเปลี่ยนความแม่นยำและการบรรจบกันในฟังก์ชันต่างๆ วิธีการที่เป็นที่นิยมเรียกว่า ผลรวมของ Riemann ที่นี่คุณจะใช้กฎรูปสี่เหลี่ยมคางหมู (คุณสามารถค้นหาโค้ดเพิ่มเติมเพื่อสำรวจวิธีการประมาณค่าต่างๆได้ในตอนท้ายของบทช่วยสอนนี้)

$ IntegratedGrads ^ {ประมาณ} _ {i} (x) :: = (x_ {i} -x '_ {i}) \ times \ overbrace {\ sum_ {k = 1} ^ {m}} ^ \ text { Sum m local gradients} \ text {gradients (interpolated images)} \ times \ overbrace {\ frac {1} {m}} ^ \ text {Divide by m steps} $

จากสมการคุณจะเห็นว่าคุณกำลังรวมกับการไล่ระดับสี m และหารด้วยขั้นตอน m คุณสามารถใช้การดำเนินการสองอย่างร่วมกันสำหรับส่วนที่ 3 โดยเป็น ค่าเฉลี่ยของการไล่ระดับสีในพื้นที่ของการคาดคะเนที่สอดแทรก m และภาพอินพุต

def integral_approximation(gradients):
  # riemann_trapezoidal
  grads = (gradients[:-1] + gradients[1:]) / tf.constant(2.0)
  integrated_gradients = tf.math.reduce_mean(grads, axis=0)
  return integrated_gradients

ฟังก์ชัน integral_approximation ใช้การไล่ระดับสีของความน่าจะเป็นที่คาดการณ์ไว้ของคลาสเป้าหมายที่เกี่ยวข้องกับภาพที่ถูกแทรกระหว่างเส้นฐานและภาพต้นฉบับ

ig = integral_approximation(
    gradients=path_gradients)

คุณสามารถยืนยันได้ว่าค่าเฉลี่ยของการไล่ระดับสีของภาพที่มีการแทรกสอด m จะส่งกลับค่าเทนเซอร์ไล่ระดับสีในตัวที่มีรูปร่างเหมือนกับภาพ "แพนด้ายักษ์" ดั้งเดิม

print(ig.shape)
(224, 224, 3)

วางมันทั้งหมดเข้าด้วยกัน

ตอนนี้คุณจะรวม 3 ส่วนทั่วไปก่อนหน้านี้เข้าด้วยกันเป็นฟังก์ชัน IntegratedGradients และใช้มัณฑนากร @ tf.function เพื่อรวบรวมเป็นกราฟ TensorFlow ที่เรียกได้ประสิทธิภาพสูง สิ่งนี้ดำเนินการเป็น 5 ขั้นตอนเล็ก ๆ ด้านล่าง:

$ IntegratedGrads ^ {ประมาณ} _ {i} (x) :: = \ overbrace {(x_ {i} -x '_ {i})} ^ \ text {5.} \ times \ overbrace {\ sum_ {k = 1} ^ {m}} ^ \ text {4.} \ frac {\ partial \ overbrace {F (\ overbrace {x '+ \ overbrace {\ frac {k} {m}} ^ \ text {1.} \ ครั้ง (x - x '))} ^ \ text {2.}} ^ \ text {3.}} {\ partial x_ {i}} \ times \ overbrace {\ frac {1} {m}} ^ \ text {4.} $

  1. สร้างอัลฟา $ \ alpha $

  2. สร้างภาพที่ถูกแก้ไข = $ (x '+ \ frac {k} {m} \ times (x - x')) $

  3. คำนวณการไล่ระดับสีระหว่างโมเดล $ F $ การคาดคะเนเอาต์พุตที่เกี่ยวกับคุณสมบัติอินพุต = $ \ frac {\ partial F (\ text {interpolated path inputs})} {\ partial x_ {i}} $

  4. การประมาณเชิงปริพันธ์ผ่านการไล่ระดับสีเฉลี่ย = $ \ sum_ {k = 1} ^ m \ text {gradients} \ times \ frac {1} {m} $

  5. ปรับขนาดการไล่ระดับสีแบบรวมตามภาพต้นฉบับ = $ (x_ {i} -x '_ {i}) \ times \ text {integrated gradients} $ เหตุผลที่จำเป็นต้องทำขั้นตอนนี้คือการตรวจสอบให้แน่ใจว่าค่าการระบุแหล่งที่มาที่สะสมในรูปภาพที่มีการแก้ไขหลายภาพอยู่ในหน่วยเดียวกันและแสดงถึงการนำเข้าพิกเซลบนรูปภาพต้นฉบับอย่างแท้จริง

@tf.function
def integrated_gradients(baseline,
                         image,
                         target_class_idx,
                         m_steps=50,
                         batch_size=32):
  # 1. Generate alphas.
  alphas = tf.linspace(start=0.0, stop=1.0, num=m_steps+1)

  # Initialize TensorArray outside loop to collect gradients.    
  gradient_batches = tf.TensorArray(tf.float32, size=m_steps+1)
    
  # Iterate alphas range and batch computation for speed, memory efficiency, and scaling to larger m_steps.
  for alpha in tf.range(0, len(alphas), batch_size):
    from_ = alpha
    to = tf.minimum(from_ + batch_size, len(alphas))
    alpha_batch = alphas[from_:to]

    # 2. Generate interpolated inputs between baseline and input.
    interpolated_path_input_batch = interpolate_images(baseline=baseline,
                                                       image=image,
                                                       alphas=alpha_batch)

    # 3. Compute gradients between model outputs and interpolated inputs.
    gradient_batch = compute_gradients(images=interpolated_path_input_batch,
                                       target_class_idx=target_class_idx)
    
    # Write batch indices and gradients to extend TensorArray.
    gradient_batches = gradient_batches.scatter(tf.range(from_, to), gradient_batch)    
  
  # Stack path gradients together row-wise into single tensor.
  total_gradients = gradient_batches.stack()

  # 4. Integral approximation through averaging gradients.
  avg_gradients = integral_approximation(gradients=total_gradients)

  # 5. Scale integrated gradients with respect to input.
  integrated_gradients = (image - baseline) * avg_gradients

  return integrated_gradients
ig_attributions = integrated_gradients(baseline=baseline,
                                       image=img_name_tensors['Fireboat'],
                                       target_class_idx=555,
                                       m_steps=240)

ตรวจสอบอีกครั้งว่าคุณลักษณะของ IG มีรูปร่างเหมือนกับภาพ "Fireboat" ที่ป้อน

print(ig_attributions.shape)
(224, 224, 3)

กระดาษแสดงจำนวนขั้นตอนในช่วงระหว่าง 20 ถึง 300 ขึ้นอยู่กับตัวอย่าง (แม้ว่าในทางปฏิบัติอาจสูงกว่าใน 1,000 วินาทีเพื่อประมาณอินทิกรัลได้อย่างแม่นยำ) คุณสามารถค้นหาโค้ดเพิ่มเติมเพื่อตรวจสอบจำนวนขั้นตอนที่เหมาะสมได้ในแหล่งข้อมูล "ขั้นตอนถัดไป" ที่ท้ายบทแนะนำนี้

เห็นภาพการระบุแหล่งที่มา

คุณพร้อมที่จะเห็นภาพการระบุแหล่งที่มาและวางซ้อนบนภาพต้นฉบับ โค้ดด้านล่างจะรวมค่าสัมบูรณ์ของการไล่ระดับสีในช่องสีเพื่อสร้างรูปแบบการระบุแหล่งที่มา วิธีการลงจุดนี้รวบรวมผลกระทบสัมพัทธ์ของพิกเซลต่อการคาดคะเนของโมเดล

def plot_img_attributions(baseline,
                          image,
                          target_class_idx,
                          m_steps=50,
                          cmap=None,
                          overlay_alpha=0.4):

  attributions = integrated_gradients(baseline=baseline,
                                      image=image,
                                      target_class_idx=target_class_idx,
                                      m_steps=m_steps)

  # Sum of the attributions across color channels for visualization.
  # The attribution mask shape is a grayscale image with height and width
  # equal to the original image.
  attribution_mask = tf.reduce_sum(tf.math.abs(attributions), axis=-1)

  fig, axs = plt.subplots(nrows=2, ncols=2, squeeze=False, figsize=(8, 8))

  axs[0, 0].set_title('Baseline image')
  axs[0, 0].imshow(baseline)
  axs[0, 0].axis('off')

  axs[0, 1].set_title('Original image')
  axs[0, 1].imshow(image)
  axs[0, 1].axis('off')

  axs[1, 0].set_title('Attribution mask')
  axs[1, 0].imshow(attribution_mask, cmap=cmap)
  axs[1, 0].axis('off')

  axs[1, 1].set_title('Overlay')
  axs[1, 1].imshow(attribution_mask, cmap=cmap)
  axs[1, 1].imshow(image, alpha=overlay_alpha)
  axs[1, 1].axis('off')

  plt.tight_layout()
  return fig

เมื่อดูการระบุแหล่งที่มาในภาพ "เรือดับเพลิง" คุณจะเห็นแบบจำลองที่ระบุว่าปืนใหญ่น้ำและพวยกามีส่วนช่วยในการทำนายที่ถูกต้อง

_ = plot_img_attributions(image=img_name_tensors['Fireboat'],
                          baseline=baseline,
                          target_class_idx=555,
                          m_steps=240,
                          cmap=plt.cm.inferno,
                          overlay_alpha=0.4)

png

ในภาพ "แพนด้ายักษ์" การระบุแหล่งที่มาจะเน้นที่พื้นผิวจมูกและขนของใบหน้าของแพนด้า

_ = plot_img_attributions(image=img_name_tensors['Giant Panda'],
                          baseline=baseline,
                          target_class_idx=389,
                          m_steps=55,
                          cmap=plt.cm.viridis,
                          overlay_alpha=0.5)

png

การใช้งานและข้อ จำกัด

ใช้กรณี

  • การใช้เทคนิคเช่นการไล่ระดับสีแบบรวมก่อนที่จะทำให้โมเดลของคุณใช้งานได้จะช่วยให้คุณพัฒนาสัญชาตญาณว่ามันทำงานอย่างไรและทำไม คุณสมบัติที่เน้นโดยเทคนิคนี้ตรงกับสัญชาตญาณของคุณหรือไม่? หากไม่เป็นเช่นนั้นอาจบ่งบอกถึงข้อบกพร่องในโมเดลหรือชุดข้อมูลของคุณหรือใส่อุปกรณ์มากเกินไป

ข้อ จำกัด

  • การไล่ระดับสีแบบรวมให้การนำเข้าคุณลักษณะในแต่ละตัวอย่างอย่างไรก็ตามไม่ได้ให้การนำเข้าคุณลักษณะทั่วโลกในชุดข้อมูลทั้งหมด

  • การไล่ระดับสีแบบรวมให้การนำเข้าคุณลักษณะแต่ละรายการ แต่ไม่ได้อธิบายการโต้ตอบและชุดค่าผสมของคุณลักษณะ

ขั้นตอนถัดไป

บทช่วยสอนนี้นำเสนอการใช้งานพื้นฐานของการไล่ระดับสีแบบรวม ในขั้นตอนต่อไปคุณสามารถใช้สมุดบันทึกนี้เพื่อลองใช้เทคนิคนี้กับโมเดลและรูปภาพต่างๆด้วยตัวคุณเอง

สำหรับผู้อ่านที่สนใจมีบทช่วยสอนนี้ในเวอร์ชันที่ยาวกว่านี้ (ซึ่งรวมถึงโค้ดสำหรับเส้นฐานต่างๆเพื่อคำนวณการประมาณค่าอินทิกรัลและกำหนดจำนวนขั้นตอนที่เพียงพอ) ซึ่งคุณสามารถดูได้ ที่นี่

หากต้องการทำความเข้าใจให้ลึกซึ้งยิ่งขึ้นโปรดดูเอกสาร Axiomatic Attribution สำหรับ Deep Networks และที่ เก็บ Github ซึ่งมีการนำไปใช้งานใน TensorFlow เวอร์ชันก่อนหน้า คุณยังสามารถสำรวจการระบุแหล่งที่มาของคุณลักษณะและผลกระทบของเส้นฐานต่างๆได้ใน distill.pub

สนใจที่จะรวม IG เข้ากับเวิร์กโฟลว์การเรียนรู้ของเครื่องการผลิตของคุณสำหรับการนำเข้าคุณลักษณะการวิเคราะห์ข้อผิดพลาดของโมเดลและการตรวจสอบข้อมูลเอียงหรือไม่? ดูผลิตภัณฑ์ AI ที่อธิบายได้ ของ Google Cloud ที่รองรับการระบุแหล่งที่มาของ IG กลุ่มวิจัย Google AI PAIR ยังเปิดแหล่งที่มาของ เครื่องมือ What-if ซึ่งสามารถใช้สำหรับการดีบักแบบจำลองรวมถึงการแสดงคุณลักษณะของคุณลักษณะ IG