דף זה תורגם על ידי Cloud Translation API.
Switch to English

אוברפיט ותחתון

צפה ב- TensorFlow.org הפעל בגוגל קולאב צפה במקור ב- GitHub הורד מחברת

כמו תמיד, הקוד בדוגמה זו ישתמש בממשק ה- API של tf.keras , עליו תוכלו ללמוד עוד במדריך TensorFlow Keras .

בשתי הדוגמאות הקודמות - סיווג טקסט וניבוי יעילות הדלק - ראינו שהדיוק של המודל שלנו על נתוני האימות יגיע לשיאו לאחר אימון למספר תקופות, ואז היה עומד בקיפאון או יתחיל לרדת.

במילים אחרות, המודל שלנו יתאים לנתוני האימון. חשוב ללמוד כיצד להתמודד עם התאמת יתר. למרות שלעתים קרובות ניתן להשיג דיוק גבוה במערך האימונים , מה שאנחנו באמת רוצים זה לפתח מודלים שמכלילים היטב מערך בדיקות (או נתונים שהם לא ראו קודם).

ההפך מאיבוד יתר הוא תת-אבזור . חיסול מתרחש כאשר עדיין יש מקום לשיפור בנתוני הבדיקה. זה יכול לקרות ממספר סיבות: אם המודל אינו מספיק חזק, הוא מוסדר יתר על המידה, או פשוט לא הוכשר מספיק זמן. המשמעות היא שהרשת לא למדה את הדפוסים הרלוונטיים בנתוני ההדרכה.

אם אתה מתאמן זמן רב מדי, המודל יתחיל להתאים יותר מדי וללמוד דפוסים מנתוני האימון שאינם מתכללים לנתוני המבחן. עלינו למצוא איזון. ההבנה כיצד להתאמן למספר תקופות מתאים כמו שנחקור להלן היא מיומנות שימושית.

כדי למנוע התאמת יתר, הפיתרון הטוב ביותר הוא להשתמש בנתוני אימון מלאים יותר. מערך הנתונים אמור לכסות את כל מגוון הקלטים שהמודל צפוי לטפל בהם. נתונים נוספים עשויים להיות שימושיים רק אם הם מכסים מקרים חדשים ומעניינים.

מודל המאומן על נתונים מלאים יותר, באופן כללי, באופן כללי טוב יותר. כאשר זה כבר לא אפשרי, הפיתרון הטוב ביותר הבא הוא להשתמש בטכניקות כמו רגולציה. אלה מציבים מגבלות על כמות וסוג המידע שהמודל שלך יכול לאחסן. אם רשת יכולה להרשות לעצמה רק לשנן מספר קטן של תבניות, תהליך האופטימיזציה יאלץ אותה להתמקד בתבניות הבולטות ביותר, שיש להן סיכוי טוב יותר להכליל היטב.

במחברת זו נחקור מספר טכניקות רגולציה רגילות ונשתמש בהן לשיפור מודל סיווג.

להכין

לפני שתתחיל, ייבא את החבילות הדרושות:

import tensorflow as tf

from tensorflow.keras import layers
from tensorflow.keras import regularizers

print(tf.__version__)
2.3.0

!pip install -q git+https://github.com/tensorflow/docs

import tensorflow_docs as tfdocs
import tensorflow_docs.modeling
import tensorflow_docs.plots
WARNING: You are using pip version 20.2.2; however, version 20.2.3 is available.
You should consider upgrading via the '/tmpfs/src/tf_docs_env/bin/python -m pip install --upgrade pip' command.

from  IPython import display
from matplotlib import pyplot as plt

import numpy as np

import pathlib
import shutil
import tempfile

logdir = pathlib.Path(tempfile.mkdtemp())/"tensorboard_logs"
shutil.rmtree(logdir, ignore_errors=True)

מערך הנתונים של היגס

מטרת הדרכה זו היא לא לעשות פיזיקת חלקיקים, לכן אל תתעכב על פרטי מערך הנתונים. הוא מכיל 11,000,000 דוגמאות, כל אחת מהן כוללת 28 תכונות, ותווית מחלקה בינארית.

gz = tf.keras.utils.get_file('HIGGS.csv.gz', 'http://mlphysics.ics.uci.edu/data/higgs/HIGGS.csv.gz')
Downloading data from http://mlphysics.ics.uci.edu/data/higgs/HIGGS.csv.gz
2816409600/2816407858 [==============================] - 195s 0us/step

FEATURES = 28

ניתן tf.data.experimental.CsvDataset במחלקה tf.data.experimental.CsvDataset לקריאת רשומות csv ישירות מקובץ gzip ללא שלב של לחץ לחץ ביניים.

ds = tf.data.experimental.CsvDataset(gz,[float(),]*(FEATURES+1), compression_type="GZIP")

אותה כיתת קוראי csv מחזירה רשימה של סקלרים לכל רשומה. הפונקציה הבאה מארזת את רשימת הסקלרים מחדש בצמד (תכונה_ווקטור, תווית).

def pack_row(*row):
  label = row[0]
  features = tf.stack(row[1:],1)
  return features, label

TensorFlow הוא היעיל ביותר כאשר פועל על קבוצות נתונים גדולות.

אז במקום לארוז מחדש כל שורה בנפרד, pack_row Dataset חדש שלוקח אצוות של 10000 דוגמאות, החל את פונקציית pack_row על כל אצווה, ואז pack_row את הקבוצות בחזרה לרשומות בודדות:

packed_ds = ds.batch(10000).map(pack_row).unbatch()

תסתכל על כמה רשומות packed_ds החדשים packed_ds .

התכונות אינן מנורמלות לחלוטין, אך זה מספיק להדרכה זו.

for features,label in packed_ds.batch(1000).take(1):
  print(features[0])
  plt.hist(features.numpy().flatten(), bins = 101)
tf.Tensor(
[ 0.8692932  -0.6350818   0.22569026  0.32747006 -0.6899932   0.75420225
 -0.24857314 -1.0920639   0.          1.3749921  -0.6536742   0.9303491
  1.1074361   1.1389043  -1.5781983  -1.0469854   0.          0.65792954
 -0.01045457 -0.04576717  3.1019614   1.35376     0.9795631   0.97807616
  0.92000484  0.72165745  0.98875093  0.87667835], shape=(28,), dtype=float32)

png

כדי לשמור על הדרכה קצרה יחסית, השתמש רק ב- 1000 הדוגמאות הראשונות לאימות, וב -10,000 הבאים לאימון:

N_VALIDATION = int(1e3)
N_TRAIN = int(1e4)
BUFFER_SIZE = int(1e4)
BATCH_SIZE = 500
STEPS_PER_EPOCH = N_TRAIN//BATCH_SIZE

השיטות Dataset.take ו- Dataset.skip Dataset.take זה.

במקביל, השתמש בשיטת Dataset.cache כדי להבטיח כי Dataset.cache לא צריך לקרוא מחדש את הנתונים מהקובץ בכל תקופה:

validate_ds = packed_ds.take(N_VALIDATION).cache()
train_ds = packed_ds.skip(N_VALIDATION).take(N_TRAIN).cache()
train_ds
<CacheDataset shapes: ((28,), ()), types: (tf.float32, tf.float32)>

מערכי נתונים אלה מחזירים דוגמאות בודדות. השתמש בשיטת .batch כדי ליצור קבוצות בגודל מתאים לאימון. לפני אצווה, זכור גם .shuffle .repeat על מערך האימונים.

validate_ds = validate_ds.batch(BATCH_SIZE)
train_ds = train_ds.shuffle(BUFFER_SIZE).repeat().batch(BATCH_SIZE)

להפגין התאמת יתר

הדרך הפשוטה ביותר למנוע התאמת יתר היא להתחיל במודל קטן: מודל עם מספר קטן של פרמטרים לומדים (שנקבע על ידי מספר השכבות ומספר היחידות לשכבה). בלמידה עמוקה, מספר הפרמטרים הניתנים ללמידה במודל מכונה לעתים קרובות "היכולת" של המודל.

באופן אינטואיטיבי, מודל עם יותר פרמטרים יהיה בעל "יכולת שינון" רבה יותר ולכן יוכל ללמוד מיפוי מושלם דמוי מילון בין דגימות האימון למטרותיהם, מיפוי ללא כל כוח הכללה, אך זה לא יועיל בעת חיזוי. על נתונים שטרם נראו.

זכור זאת תמיד: מודלים של למידה עמוקה נוטים להיות טובים בהתאמה לנתוני האימון, אך האתגר האמיתי הוא הכללה, ולא התאמה.

מצד שני, אם לרשת יש משאבי שינון מוגבלים, היא לא תוכל ללמוד את המיפוי באותה קלות. כדי למזער את אובדן, היא תצטרך ללמוד ייצוגים דחוסים בעלי כוח ניבוי רב יותר. יחד עם זאת, אם תהפוך את המודל שלך לקטן מדי, הוא יתקשה להתאים לנתוני האימון. יש איזון בין "יותר מדי יכולת" ל"לא מספיק יכולת ".

למרבה הצער, אין נוסחה קסומה לקביעת הגודל או הארכיטקטורה הנכונים של המודל שלך (מבחינת מספר השכבות, או הגודל הנכון לכל שכבה). יהיה עליכם להתנסות באמצעות סדרה של ארכיטקטורות שונות.

כדי למצוא גודל מודל מתאים, עדיף להתחיל עם מעט שכבות ופרמטרים יחסית, ואז להתחיל להגדיל את גודל השכבות או להוסיף שכבות חדשות עד שתראה ירידה בתשואות על אובדן האימות.

התחל במודל פשוט המשתמש layers.Dense בלבד. layers.Dense כבסיס, ואז צור גרסאות גדולות יותר והשווה ביניהן.

נוהל אימונים

מודלים רבים מתאמנים טוב יותר אם אתה מוריד בהדרגה את קצב הלמידה במהלך האימון. השתמש ב- optimizers.schedules להפחתת קצב הלמידה לאורך זמן:

lr_schedule = tf.keras.optimizers.schedules.InverseTimeDecay(
  0.001,
  decay_steps=STEPS_PER_EPOCH*1000,
  decay_rate=1,
  staircase=False)

def get_optimizer():
  return tf.keras.optimizers.Adam(lr_schedule)

הקוד שלמעלה קובע schedules.InverseTimeDecay InverseTimeDecay להפחית את קצב הלמידה באופן היפרבולי ל -1 / 2 משיעור הבסיס ב -1000 תקופות, 1/3 ב -2000 תקופות וכן הלאה.

step = np.linspace(0,100000)
lr = lr_schedule(step)
plt.figure(figsize = (8,6))
plt.plot(step/STEPS_PER_EPOCH, lr)
plt.ylim([0,max(plt.ylim())])
plt.xlabel('Epoch')
_ = plt.ylabel('Learning Rate')

png

כל דגם במדריך זה ישתמש באותה תצורת אימונים. אז קבעו את הדברים בצורה רב פעמית, החל מרשימת השיחות החוזרות.

ההדרכה להדרכה זו פועלת לאורך תקופות קצרות רבות. כדי להפחית את רעש הרישום השתמש ב- tfdocs.EpochDots שמדפיס פשוט a . לכל תקופה, ומערכת מדדים מלאה בכל 100 תקופות.

הבא כולל callbacks.EarlyStopping כדי למנוע זמני אימון ארוכים ומיותרים. שים לב val_binary_crossentropy זו מוגדרת לפקח על val_binary_crossentropy , ולא על val_loss . הבדל זה יהיה חשוב בהמשך.

השתמש ב- callbacks.TensorBoard כדי ליצור יומני TensorBoard לאימון.

690

בדומה לכך כל דגם ישתמש באותו Model.compile ו Model.fit הגדרות:

def compile_and_fit(model, name, optimizer=None, max_epochs=10000):
  if optimizer is None:
    optimizer = get_optimizer()
  model.compile(optimizer=optimizer,
                loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
                metrics=[
                  tf.keras.losses.BinaryCrossentropy(
                      from_logits=True, name='binary_crossentropy'),
                  'accuracy'])

  model.summary()

  history = model.fit(
    train_ds,
    steps_per_epoch = STEPS_PER_EPOCH,
    epochs=max_epochs,
    validation_data=validate_ds,
    callbacks=get_callbacks(name),
    verbose=0)
  return history

דגם זעיר

התחל באימון מודל:

tiny_model = tf.keras.Sequential([
    layers.Dense(16, activation='elu', input_shape=(FEATURES,)),
    layers.Dense(1)
])
size_histories = {}
size_histories['Tiny'] = compile_and_fit(tiny_model, 'sizes/Tiny')
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense (Dense)                (None, 16)                464       
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 17        
=================================================================
Total params: 481
Trainable params: 481
Non-trainable params: 0
_________________________________________________________________
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/summary_ops_v2.py:1277: stop (from tensorflow.python.eager.profiler) is deprecated and will be removed after 2020-07-01.
Instructions for updating:
use `tf.profiler.experimental.stop` instead.
WARNING:tensorflow:Callbacks method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0030s vs `on_train_batch_end` time: 0.0237s). Check your callbacks.

Epoch: 0, accuracy:0.4851,  binary_crossentropy:0.7694,  loss:0.7694,  val_accuracy:0.5020,  val_binary_crossentropy:0.7430,  val_loss:0.7430,  
....................................................................................................
Epoch: 100, accuracy:0.5922,  binary_crossentropy:0.6288,  loss:0.6288,  val_accuracy:0.5730,  val_binary_crossentropy:0.6331,  val_loss:0.6331,  
....................................................................................................
Epoch: 200, accuracy:0.6069,  binary_crossentropy:0.6215,  loss:0.6215,  val_accuracy:0.5820,  val_binary_crossentropy:0.6277,  val_loss:0.6277,  
....................................................................................................
Epoch: 300, accuracy:0.6131,  binary_crossentropy:0.6165,  loss:0.6165,  val_accuracy:0.6040,  val_binary_crossentropy:0.6208,  val_loss:0.6208,  
....................................................................................................
Epoch: 400, accuracy:0.6213,  binary_crossentropy:0.6111,  loss:0.6111,  val_accuracy:0.5980,  val_binary_crossentropy:0.6179,  val_loss:0.6179,  
....................................................................................................
Epoch: 500, accuracy:0.6344,  binary_crossentropy:0.6045,  loss:0.6045,  val_accuracy:0.6250,  val_binary_crossentropy:0.6092,  val_loss:0.6092,  
....................................................................................................
Epoch: 600, accuracy:0.6408,  binary_crossentropy:0.5985,  loss:0.5985,  val_accuracy:0.6170,  val_binary_crossentropy:0.6068,  val_loss:0.6068,  
....................................................................................................
Epoch: 700, accuracy:0.6454,  binary_crossentropy:0.5941,  loss:0.5941,  val_accuracy:0.6380,  val_binary_crossentropy:0.6033,  val_loss:0.6033,  
....................................................................................................
Epoch: 800, accuracy:0.6571,  binary_crossentropy:0.5907,  loss:0.5907,  val_accuracy:0.6350,  val_binary_crossentropy:0.6023,  val_loss:0.6023,  
....................................................................................................
Epoch: 900, accuracy:0.6568,  binary_crossentropy:0.5879,  loss:0.5879,  val_accuracy:0.6390,  val_binary_crossentropy:0.6022,  val_loss:0.6022,  
....................................................................................................
Epoch: 1000, accuracy:0.6592,  binary_crossentropy:0.5860,  loss:0.5860,  val_accuracy:0.6410,  val_binary_crossentropy:0.6006,  val_loss:0.6006,  
....................................................................................................
Epoch: 1100, accuracy:0.6674,  binary_crossentropy:0.5833,  loss:0.5833,  val_accuracy:0.6310,  val_binary_crossentropy:0.6020,  val_loss:0.6020,  
....................................................................................................
Epoch: 1200, accuracy:0.6681,  binary_crossentropy:0.5814,  loss:0.5814,  val_accuracy:0.6300,  val_binary_crossentropy:0.6013,  val_loss:0.6013,  
....................................................................................................
Epoch: 1300, accuracy:0.6711,  binary_crossentropy:0.5798,  loss:0.5798,  val_accuracy:0.6430,  val_binary_crossentropy:0.5985,  val_loss:0.5985,  
....................................................................................................
Epoch: 1400, accuracy:0.6723,  binary_crossentropy:0.5781,  loss:0.5781,  val_accuracy:0.6440,  val_binary_crossentropy:0.5984,  val_loss:0.5984,  
....................................................................................................
Epoch: 1500, accuracy:0.6723,  binary_crossentropy:0.5773,  loss:0.5773,  val_accuracy:0.6490,  val_binary_crossentropy:0.5969,  val_loss:0.5969,  
....................................................................................................
Epoch: 1600, accuracy:0.6710,  binary_crossentropy:0.5762,  loss:0.5762,  val_accuracy:0.6620,  val_binary_crossentropy:0.5953,  val_loss:0.5953,  
....................................................................................................
Epoch: 1700, accuracy:0.6757,  binary_crossentropy:0.5744,  loss:0.5744,  val_accuracy:0.6510,  val_binary_crossentropy:0.5956,  val_loss:0.5956,  
....................................................................................................
Epoch: 1800, accuracy:0.6771,  binary_crossentropy:0.5734,  loss:0.5734,  val_accuracy:0.6560,  val_binary_crossentropy:0.5947,  val_loss:0.5947,  
....................................................................................................
Epoch: 1900, accuracy:0.6780,  binary_crossentropy:0.5723,  loss:0.5723,  val_accuracy:0.6550,  val_binary_crossentropy:0.5942,  val_loss:0.5942,  
....................................................................................................
Epoch: 2000, accuracy:0.6794,  binary_crossentropy:0.5716,  loss:0.5716,  val_accuracy:0.6590,  val_binary_crossentropy:0.5930,  val_loss:0.5930,  
....................................................................................................
Epoch: 2100, accuracy:0.6777,  binary_crossentropy:0.5707,  loss:0.5707,  val_accuracy:0.6560,  val_binary_crossentropy:0.5938,  val_loss:0.5938,  
....................................................................................................
Epoch: 2200, accuracy:0.6817,  binary_crossentropy:0.5699,  loss:0.5699,  val_accuracy:0.6480,  val_binary_crossentropy:0.5942,  val_loss:0.5942,  
....................................................................................................
Epoch: 2300, accuracy:0.6796,  binary_crossentropy:0.5696,  loss:0.5696,  val_accuracy:0.6540,  val_binary_crossentropy:0.5922,  val_loss:0.5922,  
....................................................................................................
Epoch: 2400, accuracy:0.6823,  binary_crossentropy:0.5695,  loss:0.5695,  val_accuracy:0.6530,  val_binary_crossentropy:0.5919,  val_loss:0.5919,  
....................................................................................................
Epoch: 2500, accuracy:0.6848,  binary_crossentropy:0.5688,  loss:0.5688,  val_accuracy:0.6530,  val_binary_crossentropy:0.5943,  val_loss:0.5943,  
....................................................................................................
Epoch: 2600, accuracy:0.6837,  binary_crossentropy:0.5683,  loss:0.5683,  val_accuracy:0.6580,  val_binary_crossentropy:0.5920,  val_loss:0.5920,  
....................................................................................................
Epoch: 2700, accuracy:0.6867,  binary_crossentropy:0.5687,  loss:0.5687,  val_accuracy:0.6560,  val_binary_crossentropy:0.5938,  val_loss:0.5938,  
....................................................................................................
Epoch: 2800, accuracy:0.6874,  binary_crossentropy:0.5671,  loss:0.5671,  val_accuracy:0.6550,  val_binary_crossentropy:0.5922,  val_loss:0.5922,  
....................................................................................................
Epoch: 2900, accuracy:0.6814,  binary_crossentropy:0.5666,  loss:0.5666,  val_accuracy:0.6590,  val_binary_crossentropy:0.5907,  val_loss:0.5907,  
.......................................................

כעת בדוק כיצד הצליח המודל:

plotter = tfdocs.plots.HistoryPlotter(metric = 'binary_crossentropy', smoothing_std=10)
plotter.plot(size_histories)
plt.ylim([0.5, 0.7])
(0.5, 0.7)

png

דגם קטן

כדי לראות אם אתה יכול לנצח את ביצועי הדגם הקטן, הכשר בהדרגה כמה דגמים גדולים יותר.

נסה שתי שכבות נסתרות עם 16 יחידות כל אחת:

small_model = tf.keras.Sequential([
    # `input_shape` is only required here so that `.summary` works.
    layers.Dense(16, activation='elu', input_shape=(FEATURES,)),
    layers.Dense(16, activation='elu'),
    layers.Dense(1)
])
size_histories['Small'] = compile_and_fit(small_model, 'sizes/Small')
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_2 (Dense)              (None, 16)                464       
_________________________________________________________________
dense_3 (Dense)              (None, 16)                272       
_________________________________________________________________
dense_4 (Dense)              (None, 1)                 17        
=================================================================
Total params: 753
Trainable params: 753
Non-trainable params: 0
_________________________________________________________________
WARNING:tensorflow:Callbacks method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0034s vs `on_train_batch_end` time: 0.0481s). Check your callbacks.

Epoch: 0, accuracy:0.4760,  binary_crossentropy:0.7078,  loss:0.7078,  val_accuracy:0.4700,  val_binary_crossentropy:0.6948,  val_loss:0.6948,  
....................................................................................................
Epoch: 100, accuracy:0.6241,  binary_crossentropy:0.6134,  loss:0.6134,  val_accuracy:0.5950,  val_binary_crossentropy:0.6196,  val_loss:0.6196,  
....................................................................................................
Epoch: 200, accuracy:0.6435,  binary_crossentropy:0.5975,  loss:0.5975,  val_accuracy:0.6290,  val_binary_crossentropy:0.6116,  val_loss:0.6116,  
....................................................................................................
Epoch: 300, accuracy:0.6636,  binary_crossentropy:0.5827,  loss:0.5827,  val_accuracy:0.6310,  val_binary_crossentropy:0.6020,  val_loss:0.6020,  
....................................................................................................
Epoch: 400, accuracy:0.6742,  binary_crossentropy:0.5730,  loss:0.5730,  val_accuracy:0.6500,  val_binary_crossentropy:0.5945,  val_loss:0.5945,  
....................................................................................................
Epoch: 500, accuracy:0.6822,  binary_crossentropy:0.5670,  loss:0.5670,  val_accuracy:0.6470,  val_binary_crossentropy:0.5919,  val_loss:0.5919,  
....................................................................................................
Epoch: 600, accuracy:0.6864,  binary_crossentropy:0.5631,  loss:0.5631,  val_accuracy:0.6510,  val_binary_crossentropy:0.5909,  val_loss:0.5909,  
....................................................................................................
Epoch: 700, accuracy:0.6928,  binary_crossentropy:0.5596,  loss:0.5596,  val_accuracy:0.6600,  val_binary_crossentropy:0.5910,  val_loss:0.5910,  
....................................................................................................
Epoch: 800, accuracy:0.6965,  binary_crossentropy:0.5564,  loss:0.5564,  val_accuracy:0.6620,  val_binary_crossentropy:0.5898,  val_loss:0.5898,  
....................................................................................................
Epoch: 900, accuracy:0.7008,  binary_crossentropy:0.5544,  loss:0.5544,  val_accuracy:0.6480,  val_binary_crossentropy:0.5921,  val_loss:0.5921,  
...............................................

דגם בינוני

כעת נסה 3 שכבות נסתרות עם 64 יחידות כל אחת:

medium_model = tf.keras.Sequential([
    layers.Dense(64, activation='elu', input_shape=(FEATURES,)),
    layers.Dense(64, activation='elu'),
    layers.Dense(64, activation='elu'),
    layers.Dense(1)
])

ואמן את המודל באמצעות אותם נתונים:

size_histories['Medium']  = compile_and_fit(medium_model, "sizes/Medium")
Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_5 (Dense)              (None, 64)                1856      
_________________________________________________________________
dense_6 (Dense)              (None, 64)                4160      
_________________________________________________________________
dense_7 (Dense)              (None, 64)                4160      
_________________________________________________________________
dense_8 (Dense)              (None, 1)                 65        
=================================================================
Total params: 10,241
Trainable params: 10,241
Non-trainable params: 0
_________________________________________________________________
WARNING:tensorflow:Callbacks method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0037s vs `on_train_batch_end` time: 0.0511s). Check your callbacks.

Epoch: 0, accuracy:0.4852,  binary_crossentropy:0.6982,  loss:0.6982,  val_accuracy:0.4830,  val_binary_crossentropy:0.6815,  val_loss:0.6815,  
....................................................................................................
Epoch: 100, accuracy:0.7123,  binary_crossentropy:0.5315,  loss:0.5315,  val_accuracy:0.6540,  val_binary_crossentropy:0.5983,  val_loss:0.5983,  
....................................................................................................
Epoch: 200, accuracy:0.7796,  binary_crossentropy:0.4328,  loss:0.4328,  val_accuracy:0.6590,  val_binary_crossentropy:0.6763,  val_loss:0.6763,  
...................................................

דגם גדול

כתרגיל, אתה יכול ליצור מודל גדול עוד יותר, ולראות כמה מהר זה מתחיל להתאים יתר על המידה. לאחר מכן, בואו להוסיף למדד זה רשת שיש לה הרבה יותר קיבולת, הרבה יותר ממה שהבעיה תצדיק:

large_model = tf.keras.Sequential([
    layers.Dense(512, activation='elu', input_shape=(FEATURES,)),
    layers.Dense(512, activation='elu'),
    layers.Dense(512, activation='elu'),
    layers.Dense(512, activation='elu'),
    layers.Dense(1)
])

ושוב, הכשיר את המודל באמצעות אותם נתונים:

size_histories['large'] = compile_and_fit(large_model, "sizes/large")
Model: "sequential_3"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_9 (Dense)              (None, 512)               14848     
_________________________________________________________________
dense_10 (Dense)             (None, 512)               262656    
_________________________________________________________________
dense_11 (Dense)             (None, 512)               262656    
_________________________________________________________________
dense_12 (Dense)             (None, 512)               262656    
_________________________________________________________________
dense_13 (Dense)             (None, 1)                 513       
=================================================================
Total params: 803,329
Trainable params: 803,329
Non-trainable params: 0
_________________________________________________________________
WARNING:tensorflow:Callbacks method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0038s vs `on_train_batch_end` time: 0.0571s). Check your callbacks.

Epoch: 0, accuracy:0.5119,  binary_crossentropy:0.7993,  loss:0.7993,  val_accuracy:0.4630,  val_binary_crossentropy:0.7125,  val_loss:0.7125,  
....................................................................................................
Epoch: 100, accuracy:1.0000,  binary_crossentropy:0.0021,  loss:0.0021,  val_accuracy:0.6640,  val_binary_crossentropy:1.8146,  val_loss:1.8146,  
....................................................................................................
Epoch: 200, accuracy:1.0000,  binary_crossentropy:0.0001,  loss:0.0001,  val_accuracy:0.6630,  val_binary_crossentropy:2.4702,  val_loss:2.4702,  
............................

זממו את הפסדי האימון והאימות

הקווים המוצקים מראים את אובדן האימונים, והקווים המקווקו מראים את אובדן האימות (זכרו: אובדן אימות נמוך יותר מצביע על מודל טוב יותר).

בעוד שבניית דגם גדול יותר מעניקה לו יותר כוח, אם כוח זה אינו מוגבל איכשהו הוא יכול להתאים בקלות לסט האימונים.

בדוגמה זו, בדרך כלל, רק המודל "Tiny" מצליח להימנע מהתאמת יתר לחלוטין, וכל אחד מהדגמים הגדולים מכניסים את הנתונים במהירות רבה יותר. זה הופך להיות כל כך חמור עבור המודל "large" שאתה צריך להחליף את העלילה לסולם יומן כדי לראות באמת מה קורה.

זה ניכר אם אתה מתכנן ומשווה את מדדי האימות למדדי ההדרכה.

  • זה נורמלי שיש הבדל קטן.
  • אם שני המדדים נעים באותו כיוון, הכל בסדר.
  • אם מדד האימות מתחיל להיסגר בעוד מדד האימונים ממשיך להשתפר, קרוב לוודאי שאתה קרוב להתאמת יתר.
  • אם מדד האימות עובר לכיוון הלא נכון, המודל בבירור מתאים מדי.
plotter.plot(size_histories)
a = plt.xscale('log')
plt.xlim([5, max(plt.xlim())])
plt.ylim([0.5, 0.7])
plt.xlabel("Epochs [Log Scale]")
Text(0.5, 0, 'Epochs [Log Scale]')

png

צפה ב- TensorBoard

מודלים אלה כתבו כולם יומני TensorBoard במהלך האימון.

פתח מציג TensorBoard מוטבע בתוך מחברת:


# Load the TensorBoard notebook extension
%load_ext tensorboard

# Open an embedded TensorBoard viewer
%tensorboard --logdir {logdir}/sizes

אתה יכול להציג את התוצאות של ריצה קודמת של מחברת זו ב- TensorBoard.dev .

TensorBoard.dev הוא חוויה מנוהלת לאירוח, מעקב ושיתוף ניסויי ML עם כולם.

זה נכלל גם ב- <iframe> מטעמי נוחות:

display.IFrame(
    src="https://tensorboard.dev/experiment/vW7jmmF9TmKmy3rbheMQpw/#scalars&_smoothingWeight=0.97",
    width="100%", height="800px")

אם ברצונך לשתף תוצאות של TensorBoard תוכל להעלות את היומנים ל- TensorBoard.dev על ידי העתקת הדברים הבאים לתא קוד.

tensorboard dev upload --logdir  {logdir}/sizes

אסטרטגיות למניעת התאמת יתר

לפני שנכנס לתוכן בסעיף זה העתק את יומני האימונים מהמודל "Tiny" לעיל, כדי להשתמש בתור בסיס להשוואה.

shutil.rmtree(logdir/'regularizers/Tiny', ignore_errors=True)
shutil.copytree(logdir/'sizes/Tiny', logdir/'regularizers/Tiny')
PosixPath('/tmp/tmpnnkr5005/tensorboard_logs/regularizers/Tiny')
regularizer_histories = {}
regularizer_histories['Tiny'] = size_histories['Tiny']

הוסף קביעות משקל

יתכן שאתה מכיר את עקרון הגילוח של Occam: בהינתן שני הסברים למשהו, ההסבר ככל הנראה הנכון הוא ה"פשוט ביותר ", ההנחות המינימליות ביותר. זה חל גם על המודלים שנלמדו על ידי רשתות עצביות: בהתחשב בנתוני אימונים מסוימים ובארכיטקטורת רשת, ישנן קבוצות מרובות של ערכי משקולות (מודלים מרובים) שיכולים להסביר את הנתונים, ומודלים פשוטים נוטים פחות להתאים יותר מאשר מורכבים.

"מודל פשוט" בהקשר זה הוא מודל שבו בהתפלגות ערכי הפרמטרים יש פחות אנטרופיה (או מודל עם פחות פרמטרים לגמרי, כפי שראינו בסעיף לעיל). לפיכך, דרך נפוצה למתן התאמת יתר היא לשים אילוצים על מורכבותה של רשת על ידי אילוץ משקולותיה רק ​​לקחת ערכים קטנים, מה שהופך את חלוקת ערכי המשקל ל"רגילה "יותר. זה נקרא "רגולציה של משקל", וזה נעשה על ידי הוספת לפונקציית ההפסד של הרשת עלות הקשורה למשקולות גדולים. עלות זו מגיעה בשני טעמים:

  • רגולציה L1 , כאשר העלות שנוספה היא פרופורציונאלית לערכם המוחלט של מקדמי המשקולות (כלומר למה שמכונה "נורמת L1" של המשקולות).

  • רגולציה L2 , כאשר העלות שנוספה היא פרופורציונאלית לריבוע הערך של מקדמי המשקולות (כלומר למה שמכונה בריבוע "נורמת L2" של המשקולות). רגולציה של L2 נקראת גם ריקבון משקל בהקשר לרשתות עצביות. אל תתנו לשם השונה לבלבל אתכם: דעיכת משקל זהה באופן מתמטי בדיוק להסדרת L2.

ויסות L1 דוחף משקולות לעבר אפס בדיוק ומעודד מודל דליל. ויסות L2 יעניש את פרמטרי המשקולות מבלי להפוך אותם לדלילים מכיוון שהעונש הולך לאפס למשקולות קטנות. אחת הסיבות מדוע L2 נפוץ יותר.

ב- tf.keras , tf.keras משקל מתווספת על ידי העברת מופעי tf.keras של משקל לשכבות כארגומנטים של מילות מפתח. בואו נוסיף עכשיו ויסות משקל L2.

l2_model = tf.keras.Sequential([
    layers.Dense(512, activation='elu',
                 kernel_regularizer=regularizers.l2(0.001),
                 input_shape=(FEATURES,)),
    layers.Dense(512, activation='elu',
                 kernel_regularizer=regularizers.l2(0.001)),
    layers.Dense(512, activation='elu',
                 kernel_regularizer=regularizers.l2(0.001)),
    layers.Dense(512, activation='elu',
                 kernel_regularizer=regularizers.l2(0.001)),
    layers.Dense(1)
])

regularizer_histories['l2'] = compile_and_fit(l2_model, "regularizers/l2")
Model: "sequential_4"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_14 (Dense)             (None, 512)               14848     
_________________________________________________________________
dense_15 (Dense)             (None, 512)               262656    
_________________________________________________________________
dense_16 (Dense)             (None, 512)               262656    
_________________________________________________________________
dense_17 (Dense)             (None, 512)               262656    
_________________________________________________________________
dense_18 (Dense)             (None, 1)                 513       
=================================================================
Total params: 803,329
Trainable params: 803,329
Non-trainable params: 0
_________________________________________________________________
WARNING:tensorflow:Callbacks method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0039s vs `on_train_batch_end` time: 0.0601s). Check your callbacks.

Epoch: 0, accuracy:0.5034,  binary_crossentropy:0.7595,  loss:2.2567,  val_accuracy:0.5360,  val_binary_crossentropy:0.6737,  val_loss:2.0767,  
....................................................................................................
Epoch: 100, accuracy:0.6562,  binary_crossentropy:0.5978,  loss:0.6212,  val_accuracy:0.6240,  val_binary_crossentropy:0.5912,  val_loss:0.6144,  
....................................................................................................
Epoch: 200, accuracy:0.6610,  binary_crossentropy:0.5914,  loss:0.6147,  val_accuracy:0.6480,  val_binary_crossentropy:0.5813,  val_loss:0.6055,  
....................................................................................................
Epoch: 300, accuracy:0.6794,  binary_crossentropy:0.5768,  loss:0.5998,  val_accuracy:0.6730,  val_binary_crossentropy:0.5780,  val_loss:0.6009,  
....................................................................................................
Epoch: 400, accuracy:0.6843,  binary_crossentropy:0.5685,  loss:0.5914,  val_accuracy:0.6760,  val_binary_crossentropy:0.5798,  val_loss:0.6027,  
....................................................................................................
Epoch: 500, accuracy:0.6971,  binary_crossentropy:0.5602,  loss:0.5856,  val_accuracy:0.6600,  val_binary_crossentropy:0.5855,  val_loss:0.6107,  
................................................................................................

l2(0.001) פירושו שכל מקדם במטריצת המשקל של השכבה יוסיף 0.001 * weight_coefficient_value**2 לאובדן הכולל של הרשת.

זו הסיבה שאנחנו עוקבים ישירות אחר binary_crossentropy . מכיוון שלא מורכב רכיב הרגולציה הזה.

אז, אותו דגם "Large" עם עונש הסדרה L2 מבצע הרבה יותר טוב:

plotter.plot(regularizer_histories)
plt.ylim([0.5, 0.7])
(0.5, 0.7)

png

כפי שאתה יכול לראות, הדגם הרגולרי "L2" תחרותי הרבה יותר עם הדגם "Tiny" . דגם "L2" זה גם עמיד בפני התאמת יתר בהרבה מהדגם "Large" עליו התבסס למרות שיש מספר פרמטרים זהה.

עוד מידע

ישנם שני דברים חשובים שיש לציין לגבי סוג זה של רגולציה.

ראשית: אם אתה כותב לולאת אימונים משלך, עליך להיות בטוח לבקש מהמודל את הפסדי ההסדרה שלו.

result = l2_model(features)
regularization_loss=tf.add_n(l2_model.losses)

שנית: יישום זה פועל על ידי הוספת קנסות המשקל לאובדן המודל, ולאחר מכן החלת הליך אופטימיזציה סטנדרטי לאחר מכן.

יש גישה שנייה שבמקום זאת מריץ את האופטימיזציה רק ​​על ההפסד הגולמי, ואז תוך כדי יישום השלב המחושב, מייעל האופטימיזציה גם ירידה מסוימת במשקל. זה "דעיכה משקל צימוד" נתפסת אופטימיזציה כמו optimizers.FTRL ו optimizers.AdamW .

הוסף נשירה

נשירה היא אחת מטכניקות הסדירתיות היעילות והנפוצות ביותר עבור רשתות עצביות, שפותחה על ידי הינטון וסטודנטים שלו באוניברסיטת טורונטו.

ההסבר האינטואיטיבי לנשירה הוא שמכיוון שצמתים בודדים ברשת אינם יכולים להסתמך על הפלט של האחרים, כל צומת חייב להפיק תכונות שימושיות בפני עצמן.

נשירה, מוחלת על שכבה, מורכבת מ"נשירה "באופן אקראי (כלומר מוגדר לאפס) מספר תכונות פלט של השכבה במהלך האימון. נניח ששכבה נתונה בדרך כלל הייתה מחזירה וקטור [0.2, 0.5, 1.3, 0.8, 1.1] עבור מדגם קלט נתון במהלך האימון; לאחר החלת הנשירה, וקטור זה יכיל כמה אפס ערכים המופצים באופן אקראי, למשל [0, 0.5, 1.3, 0, 1.1].

"שיעור הנשירה" הוא שבר התכונות שמתאפסות; בדרך כלל הוא מוגדר בין 0.2 ל 0.5. בזמן הבדיקה, לא נשמטות יחידות, ובמקום זאת ערכי הפלט של השכבה מוקטנים על ידי גורם השווה לקצב הנשירה, כדי לאזן את העובדה שיותר יחידות פעילות מאשר בזמן האימון.

ב- tf.keras ניתן להציג נשירה ברשת באמצעות שכבת הנשירה, אשר מוחלת על פלט השכבה ממש לפני.

בואו נוסיף שתי שכבות נשירה ברשת שלנו כדי לראות עד כמה הן מצליחות להפחית התאמת יתר:

dropout_model = tf.keras.Sequential([
    layers.Dense(512, activation='elu', input_shape=(FEATURES,)),
    layers.Dropout(0.5),
    layers.Dense(512, activation='elu'),
    layers.Dropout(0.5),
    layers.Dense(512, activation='elu'),
    layers.Dropout(0.5),
    layers.Dense(512, activation='elu'),
    layers.Dropout(0.5),
    layers.Dense(1)
])

regularizer_histories['dropout'] = compile_and_fit(dropout_model, "regularizers/dropout")
Model: "sequential_5"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_19 (Dense)             (None, 512)               14848     
_________________________________________________________________
dropout (Dropout)            (None, 512)               0         
_________________________________________________________________
dense_20 (Dense)             (None, 512)               262656    
_________________________________________________________________
dropout_1 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_21 (Dense)             (None, 512)               262656    
_________________________________________________________________
dropout_2 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_22 (Dense)             (None, 512)               262656    
_________________________________________________________________
dropout_3 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_23 (Dense)             (None, 1)                 513       
=================================================================
Total params: 803,329
Trainable params: 803,329
Non-trainable params: 0
_________________________________________________________________
WARNING:tensorflow:Callbacks method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0045s vs `on_train_batch_end` time: 0.0648s). Check your callbacks.

Epoch: 0, accuracy:0.4955,  binary_crossentropy:0.8108,  loss:0.8108,  val_accuracy:0.4970,  val_binary_crossentropy:0.6725,  val_loss:0.6725,  
....................................................................................................
Epoch: 100, accuracy:0.6590,  binary_crossentropy:0.5943,  loss:0.5943,  val_accuracy:0.6730,  val_binary_crossentropy:0.5780,  val_loss:0.5780,  
....................................................................................................
Epoch: 200, accuracy:0.6894,  binary_crossentropy:0.5594,  loss:0.5594,  val_accuracy:0.6820,  val_binary_crossentropy:0.5753,  val_loss:0.5753,  
....................................................................................................
Epoch: 300, accuracy:0.7231,  binary_crossentropy:0.5111,  loss:0.5111,  val_accuracy:0.6830,  val_binary_crossentropy:0.6013,  val_loss:0.6013,  
.....................
plotter.plot(regularizer_histories)
plt.ylim([0.5, 0.7])
(0.5, 0.7)

png

ברור מעלילה זו ששתי גישות ההסדרה הללו משפרות את התנהגות המודל "Large" . אבל זה עדיין לא מכה אפילו את קו הבסיס "Tiny" .

לאחר מכן נסה את שניהם, ביחד, ובדוק אם זה משתפר.

נשירה משולבת L2 +

combined_model = tf.keras.Sequential([
    layers.Dense(512, kernel_regularizer=regularizers.l2(0.0001),
                 activation='elu', input_shape=(FEATURES,)),
    layers.Dropout(0.5),
    layers.Dense(512, kernel_regularizer=regularizers.l2(0.0001),
                 activation='elu'),
    layers.Dropout(0.5),
    layers.Dense(512, kernel_regularizer=regularizers.l2(0.0001),
                 activation='elu'),
    layers.Dropout(0.5),
    layers.Dense(512, kernel_regularizer=regularizers.l2(0.0001),
                 activation='elu'),
    layers.Dropout(0.5),
    layers.Dense(1)
])

regularizer_histories['combined'] = compile_and_fit(combined_model, "regularizers/combined")
Model: "sequential_6"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_24 (Dense)             (None, 512)               14848     
_________________________________________________________________
dropout_4 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_25 (Dense)             (None, 512)               262656    
_________________________________________________________________
dropout_5 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_26 (Dense)             (None, 512)               262656    
_________________________________________________________________
dropout_6 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_27 (Dense)             (None, 512)               262656    
_________________________________________________________________
dropout_7 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_28 (Dense)             (None, 1)                 513       
=================================================================
Total params: 803,329
Trainable params: 803,329
Non-trainable params: 0
_________________________________________________________________
WARNING:tensorflow:Callbacks method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0041s vs `on_train_batch_end` time: 0.0627s). Check your callbacks.

Epoch: 0, accuracy:0.5101,  binary_crossentropy:0.7867,  loss:0.9452,  val_accuracy:0.5440,  val_binary_crossentropy:0.6681,  val_loss:0.8258,  
....................................................................................................
Epoch: 100, accuracy:0.6491,  binary_crossentropy:0.6032,  loss:0.6321,  val_accuracy:0.6660,  val_binary_crossentropy:0.5830,  val_loss:0.6116,  
....................................................................................................
Epoch: 200, accuracy:0.6667,  binary_crossentropy:0.5912,  loss:0.6171,  val_accuracy:0.6850,  val_binary_crossentropy:0.5687,  val_loss:0.5946,  
....................................................................................................
Epoch: 300, accuracy:0.6718,  binary_crossentropy:0.5828,  loss:0.6106,  val_accuracy:0.6840,  val_binary_crossentropy:0.5667,  val_loss:0.5945,  
....................................................................................................
Epoch: 400, accuracy:0.6750,  binary_crossentropy:0.5770,  loss:0.6067,  val_accuracy:0.6870,  val_binary_crossentropy:0.5534,  val_loss:0.5832,  
....................................................................................................
Epoch: 500, accuracy:0.6733,  binary_crossentropy:0.5752,  loss:0.6071,  val_accuracy:0.6910,  val_binary_crossentropy:0.5526,  val_loss:0.5846,  
....................................................................................................
Epoch: 600, accuracy:0.6895,  binary_crossentropy:0.5634,  loss:0.5976,  val_accuracy:0.7060,  val_binary_crossentropy:0.5466,  val_loss:0.5809,  
....................................................................................................
Epoch: 700, accuracy:0.6876,  binary_crossentropy:0.5590,  loss:0.5940,  val_accuracy:0.6860,  val_binary_crossentropy:0.5502,  val_loss:0.5852,  
....................................................................................................
Epoch: 800, accuracy:0.6921,  binary_crossentropy:0.5594,  loss:0.5956,  val_accuracy:0.6990,  val_binary_crossentropy:0.5496,  val_loss:0.5858,  
....................................................................................................
Epoch: 900, accuracy:0.6900,  binary_crossentropy:0.5603,  loss:0.5975,  val_accuracy:0.7000,  val_binary_crossentropy:0.5393,  val_loss:0.5765,  
....................................................................................................
Epoch: 1000, accuracy:0.6946,  binary_crossentropy:0.5592,  loss:0.5975,  val_accuracy:0.6750,  val_binary_crossentropy:0.5564,  val_loss:0.5947,  
....................................................................................................
Epoch: 1100, accuracy:0.7000,  binary_crossentropy:0.5476,  loss:0.5872,  val_accuracy:0.7030,  val_binary_crossentropy:0.5460,  val_loss:0.5856,  
....................................................................................................
Epoch: 1200, accuracy:0.7045,  binary_crossentropy:0.5474,  loss:0.5879,  val_accuracy:0.6860,  val_binary_crossentropy:0.5480,  val_loss:0.5886,  
...........
plotter.plot(regularizer_histories)
plt.ylim([0.5, 0.7])
(0.5, 0.7)

png

מודל זה עם "Combined" הוא ללא ספק הטוב ביותר עד כה.

צפה ב- TensorBoard

דגמים אלה הקליטו גם יומני TensorBoard.

כדי לפתוח מציג טנורבורד מוטבע בתוך מחברת, העתק את הדברים הבאים לתא קוד:

%tensorboard --logdir {logdir}/regularizers

אתה יכול להציג את התוצאות של ריצה קודמת של מחברת זו ב- TensorDoard.dev .

זה נכלל גם ב- <iframe> מטעמי נוחות:

display.IFrame(
    src="https://tensorboard.dev/experiment/fGInKDo8TXes1z7HQku9mw/#scalars&_smoothingWeight=0.97",
    width = "100%",
    height="800px")

זה הועלה עם:

tensorboard dev upload --logdir  {logdir}/regularizers

מסקנות

לסיכום: להלן הדרכים הנפוצות ביותר למניעת התאמת יתר ברשתות עצביות:

  • קבל נתוני אימון נוספים.
  • צמצם את קיבולת הרשת.
  • הוסף קביעות משקל.
  • הוסף נשירה.

שתי גישות חשובות שלא נכללות במדריך זה הן:

  • הגדלת נתונים
  • נורמליזציה אצווה

זכרו שכל שיטה יכולה לעזור בפני עצמה, אך לעיתים קרובות שילוב ביניהן יכול להיות יעיל עוד יותר.


#
# Copyright (c) 2017 François Chollet
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.