Tăng tốc độ học tập với sự phân rã Cosine trong Keras/TensorFlow PlatoBlockchain Data Intelligence. Tìm kiếm dọc. Ái.

Tốc độ học tập khởi động với Cosine Decay trong Keras / TensorFlow

Tốc độ học là một siêu tham số quan trọng trong mạng học sâu – và nó trực tiếp quyết định trình độ mà các cập nhật đối với trọng lượng được thực hiện, được ước tính để giảm thiểu một số chức năng mất mát nhất định. Tính bằng SGD:

$$
trọng lượng_{t+1} = trọng lượng_t – lr * frac{derror}{dweight_t}
$$

Với tốc độ học tập là 0, trọng lượng được cập nhật sẽ quay trở lại chính nó – trọng lượngt. Tốc độ học tập thực sự là một núm mà chúng ta có thể xoay để bật hoặc tắt việc học và nó có ảnh hưởng lớn đến mức độ học tập đang diễn ra, bằng cách kiểm soát trực tiếp mức độ cập nhật trọng lượng.

Các trình tối ưu hóa khác nhau sử dụng tốc độ học tập khác nhau – nhưng khái niệm cơ bản vẫn giống nhau. Không cần phải nói, tỷ lệ học tập đã là đối tượng của nhiều nghiên cứu, bài báo và tiêu chuẩn của người thực hành.

Nói chung, hầu hết mọi người đều đồng ý rằng tốc độ học tĩnh sẽ không cắt giảm được và một số loại giảm tốc độ học xảy ra trong hầu hết các kỹ thuật điều chỉnh tốc độ học trong quá trình đào tạo - cho dù đây là loại đơn điệu, cosin, tam giác hay các loại khác. sự giảm bớt.

Một kỹ thuật mà trong những năm gần đây đã và đang có chỗ đứng là tốc độ học tập khởi động, thực tế có thể được ghép nối với bất kỳ kỹ thuật giảm thiểu nào khác.

Tốc độ học tập khởi động

Ý tưởng đằng sau việc tăng tốc độ học tập rất đơn giản. Trong giai đoạn tập luyện đầu tiên – tạ không còn ở trạng thái lý tưởng nữa. Điều này có nghĩa là các bản cập nhật lớn trên toàn diện, có thể được coi là "chỉnh sửa quá mức" cho từng mức tạ - trong đó việc cập nhật mạnh mẽ của mức tạ khác có thể phủ nhận việc cập nhật một số mức tạ khác, khiến giai đoạn tập luyện ban đầu trở nên không ổn định hơn.

Những thay đổi này rất khó xảy ra, nhưng có thể tránh được bằng cách bắt đầu có một tốc độ học tập nhỏ, đạt đến trạng thái dưới mức tối ưu ổn định hơn, và sau đó áp dụng một tốc độ học tập lớn hơn. Bạn có thể giúp mạng dễ dàng cập nhật hơn là sử dụng chúng.

Đó là khởi động tốc độ học tập! Bắt đầu với tốc độ học tập thấp (hoặc 0) và tăng lên tốc độ học tập bắt đầu (dù sao thì bạn cũng nên bắt đầu với tốc độ nào). Sự gia tăng này thực sự có thể tuân theo bất kỳ hàm số nào, nhưng thường là tuyến tính.

Sau khi đạt đến tốc độ ban đầu, các lịch trình khác như phân rã cosin, giảm tuyến tính, v.v. có thể được áp dụng để giảm dần tốc độ xuống cho đến khi kết thúc quá trình huấn luyện. Khởi động tốc độ học tập thường là một phần của lịch trình gồm hai lịch trình, trong đó lịch trình khởi động LR là lịch trình đầu tiên, trong khi một lịch trình khác sẽ tiếp tục sau khi tốc độ đã đạt đến điểm bắt đầu.

Trong hướng dẫn này, chúng tôi sẽ triển khai quá trình khởi động tốc độ học tập trong Keras/TensorFlow dưới dạng keras.optimizers.schedules.LearningRateSchedule lớp con và keras.callbacks.Callback gọi lại. Tỷ lệ học tập sẽ được tăng lên từ 0 đến target_lr và áp dụng phân rã cosine, vì đây là một lịch trình thứ cấp rất phổ biến. Như thường lệ, Keras giúp việc triển khai các giải pháp linh hoạt trở nên đơn giản theo nhiều cách khác nhau và vận chuyển chúng cùng với mạng của bạn.

Lưu ý: Việc triển khai là chung chung và được lấy cảm hứng từ Triển khai Keras của Tony trong số những mánh khóe được nêu trong “Túi thủ thuật phân loại hình ảnh bằng mạng nơ-ron tích chập”.

Tỷ lệ học tập với Keras Callbacks

Cách đơn giản nhất để thực hiện bất kỳ lịch trình tốc độ học tập nào là tạo một hàm có lr tham số (float32), chuyển nó qua một số biến đổi và trả về nó. Chức năng này sau đó được chuyển cho LearningRateScheduler gọi lại, áp dụng chức năng cho tỷ lệ học tập.

Bây giờ, tf.keras.callbacks.LearningRateScheduler() chuyển số kỷ nguyên cho hàm mà nó sử dụng để tính toán tốc độ học, khá thô. Khởi động LR nên được thực hiện trên mỗi bước (đợt), không phải kỷ nguyên, vì vậy chúng ta sẽ phải rút ra một global_step (trên tất cả các kỷ nguyên) để tính toán tốc độ học tập thay vào đó và phân loại Callback class để tạo một lệnh gọi lại tùy chỉnh thay vì chỉ truyền hàm, vì chúng ta sẽ cần truyền các đối số cho mỗi lệnh gọi, điều này là không thể khi chỉ truyền hàm:

def func():
    return ...
    
keras.callbacks.LearningRateScheduler(func)

Cách tiếp cận này thuận lợi khi bạn không muốn có mức độ tùy chỉnh cao và bạn không muốn can thiệp vào cách Keras xử lý lrvà đặc biệt nếu bạn muốn sử dụng các lệnh gọi lại như ReduceLROnPlateau() vì nó chỉ có thể hoạt động với lr. Hãy triển khai quá trình khởi động tốc độ học tập bằng cách sử dụng lệnh gọi lại Keras, bắt đầu bằng chức năng tiện lợi:

def lr_warmup_cosine_decay(global_step,
                           warmup_steps,
                           hold = 0,
                           total_steps=0,
                           start_lr=0.0,
                           target_lr=1e-3):
    
    learning_rate = 0.5 * target_lr * (1 + np.cos(np.pi * (global_step - warmup_steps - hold) / float(total_steps - warmup_steps - hold)))

    
    warmup_lr = target_lr * (global_step / warmup_steps)

    
    
    if hold > 0:
        learning_rate = np.where(global_step > warmup_steps + hold,
                                 learning_rate, target_lr)
    
    learning_rate = np.where(global_step < warmup_steps, warmup_lr, learning_rate)
    return learning_rate

Trên mỗi bước, chúng tôi tính toán tốc độ học tập và tốc độ học tập khởi động (cả hai yếu tố của lịch trình), liên quan đến start_lrtarget_lr. start_lr thường sẽ bắt đầu lúc 0.0, trong khi target_lr phụ thuộc vào mạng và trình tối ưu hóa của bạn – 1e-3 có thể không phải là một mặc định tốt, vì vậy hãy đảm bảo đặt LR bắt đầu mục tiêu của bạn khi gọi phương thức.

Nếu global_step trong đào tạo cao hơn warmup_steps chúng tôi đã thiết lập – chúng tôi sử dụng lịch trình phân rã cosine LR. Nếu không, điều đó có nghĩa là chúng ta vẫn đang khởi động, vì vậy LR khởi động sẽ được sử dụng. Nếu hold đối số được thiết lập, chúng tôi sẽ giữ target_lr cho số bước đó sau khi khởi động và trước khi phân rã cosin. np.where() cung cấp một cú pháp tuyệt vời cho việc này:

np.where(condition, value_if_true, value_if_false)

Bạn có thể hình dung chức năng với:

steps = np.arange(0, 1000, 1)
lrs = []

for step in steps:
  lrs.append(lr_warmup_cosine_decay(step, total_steps=len(steps), warmup_steps=100, hold=10))
plt.plot(lrs)

Bây giờ, chúng ta sẽ muốn sử dụng chức năng này như một phần của lệnh gọi lại và chuyển bước tối ưu hóa làm bước global_step thay vì một phần tử của một mảng tùy ý – hoặc bạn có thể thực hiện phép tính trong lớp. Hãy phân loại các Callback lớp học:

from keras import backend as K

class WarmupCosineDecay(keras.callbacks.Callback):
    def __init__(self, total_steps=0, warmup_steps=0, start_lr=0.0, target_lr=1e-3, hold=0):

        super(WarmupCosineDecay, self).__init__()
        self.start_lr = start_lr
        self.hold = hold
        self.total_steps = total_steps
        self.global_step = 0
        self.target_lr = target_lr
        self.warmup_steps = warmup_steps
        self.lrs = []

    def on_batch_end(self, batch, logs=None):
        self.global_step = self.global_step + 1
        lr = model.optimizer.lr.numpy()
        self.lrs.append(lr)

    def on_batch_begin(self, batch, logs=None):
        lr = lr_warmup_cosine_decay(global_step=self.global_step,
                                    total_steps=self.total_steps,
                                    warmup_steps=self.warmup_steps,
                                    start_lr=self.start_lr,
                                    target_lr=self.target_lr,
                                    hold=self.hold)
        K.set_value(self.model.optimizer.lr, lr)

Xem hướng dẫn thực hành, thực tế của chúng tôi để học Git, với các phương pháp hay nhất, các tiêu chuẩn được ngành công nghiệp chấp nhận và bảng lừa đảo đi kèm. Dừng lệnh Googling Git và thực sự học nó!

Đầu tiên, chúng ta định nghĩa hàm tạo cho lớp và theo dõi các trường của nó. Trên mỗi đợt kết thúc, chúng tôi sẽ tăng bước tổng thể, ghi lại LR hiện tại và thêm nó vào danh sách LR cho đến nay. Khi bắt đầu mỗi đợt – chúng tôi sẽ tính toán LR bằng cách sử dụng lr_warmup_cosine_decay() hoạt động và đặt LR đó làm LR hiện tại của trình tối ưu hóa. Việc này được thực hiện với phần phụ trợ set_value().

Khi đã xong – chỉ cần tính tổng số bước (độ dài/kích thước lô*kỷ nguyên) và lấy một phần của số đó cho warmup_steps:


total_steps = len(train_set)*config['EPOCHS']



warmup_steps = int(0.05*total_steps)

callback = WarmupCosineDecay(total_steps=total_steps, 
                             warmup_steps=warmup_steps,
                             hold=int(warmup_steps/2), 
                             start_lr=0.0, 
                             target_lr=1e-3)

Cuối cùng, xây dựng mô hình của bạn và cung cấp lệnh gọi lại trong fit() gọi:

model = keras.applications.EfficientNetV2B0(weights=None, 
                                            classes=n_classes, 
                                            input_shape=[224, 224, 3])
  
model.compile(loss="sparse_categorical_crossentropy",
                  optimizer='adam',
                  jit_compile=True,
                  metrics=['accuracy'])

Khi kết thúc khóa đào tạo, bạn có thể thu thập và hình dung các LR đã thay đổi thông qua:

lrs = callback.lrs 
plt.plot(lrs)

Tăng tốc độ học tập với sự phân rã Cosine trong Keras/TensorFlow PlatoBlockchain Data Intelligence. Tìm kiếm dọc. Ái.

Nếu bạn vẽ lịch sử của một mô hình được huấn luyện có và không có khởi động LR – bạn sẽ thấy sự khác biệt rõ rệt về độ ổn định của quá trình huấn luyện:

Tăng tốc độ học tập với sự phân rã Cosine trong Keras/TensorFlow PlatoBlockchain Data Intelligence. Tìm kiếm dọc. Ái.

Tỷ lệ học tập với Lớp con LearningRateSchedule

Một thay thế cho việc tạo một cuộc gọi lại là tạo một LearningRateSchedule lớp con, không thao tác với LR – nó thay thế nó. Cách tiếp cận này cho phép bạn nghiên cứu thêm một chút về phần phụ trợ của Keras/TensorFlow, nhưng khi sử dụng, không thể kết hợp với các lệnh gọi lại liên quan đến LR khác, chẳng hạn như ReduceLROnPlateau(), xử lý LR dưới dạng số dấu phẩy động.

Ngoài ra, việc sử dụng lớp con sẽ yêu cầu bạn làm cho nó có thể tuần tự hóa (quá tải get_config()) khi nó trở thành một phần của mô hình, nếu bạn muốn lưu trọng số của mô hình. Một điều khác cần lưu ý là lớp sẽ mong đợi làm việc riêng với tf.TensorS. Rất may, sự khác biệt duy nhất trong cách chúng tôi làm việc sẽ gọi là tf.func() thay vì np.func() vì các API TensorFlow và NumPy rất giống nhau và tương thích.

Hãy viết lại sự thuận tiện lr_warmup_cosine_decay() để sử dụng các hoạt động TensorFlow thay thế:

def lr_warmup_cosine_decay(global_step,
                           warmup_steps,
                           hold = 0,
                           total_steps=0,
                           start_lr=0.0,
                           target_lr=1e-3):
    
    
    learning_rate = 0.5 * target_lr * (1 + tf.cos(tf.constant(np.pi) * (global_step - warmup_steps - hold) / float(total_steps - warmup_steps - hold)))

    
    warmup_lr = target_lr * (global_step / warmup_steps)

    
    
    if hold > 0:
        learning_rate = tf.where(global_step > warmup_steps + hold,
                                 learning_rate, target_lr)
    
    learning_rate = tf.where(global_step < warmup_steps, warmup_lr, learning_rate)
    return learning_rate

Với hàm convinience, chúng ta có thể phân loại LearningRateSchedule lớp. Trên mỗi __call__() (đợt), chúng tôi sẽ tính toán LR bằng hàm và trả về nó. Bạn cũng có thể gói phép tính vào trong lớp được phân lớp một cách tự nhiên.

Cú pháp rõ ràng hơn Callback sublcass, chủ yếu vì chúng tôi có quyền truy cập vào step trường, thay vì tự mình theo dõi nó, mà còn khiến việc làm việc với các thuộc tính lớp trở nên khó khăn hơn một chút - đặc biệt, nó gây khó khăn cho việc trích xuất lr từ một tf.Tensor() vào bất kỳ loại nào khác để theo dõi trong danh sách. Điều này có thể được tránh về mặt kỹ thuật bằng cách chạy ở chế độ háo hức, nhưng gây khó chịu cho việc theo dõi LR cho mục đích gỡ lỗi và tốt nhất nên tránh:

class WarmUpCosineDecay(keras.optimizers.schedules.LearningRateSchedule):
    def __init__(self, start_lr, target_lr, warmup_steps, total_steps, hold):
        super().__init__()
        self.start_lr = start_lr
        self.target_lr = target_lr
        self.warmup_steps = warmup_steps
        self.total_steps = total_steps
        self.hold = hold

    def __call__(self, step):
        lr = lr_warmup_cosine_decay(global_step=step,
                                    total_steps=self.total_steps,
                                    warmup_steps=self.warmup_steps,
                                    start_lr=self.start_lr,
                                    target_lr=self.target_lr,
                                    hold=self.hold)

        return tf.where(
            step > self.total_steps, 0.0, lr, name="learning_rate"
        )

Các tham số giống nhau và có thể được tính toán theo cách tương tự như trước đây:


total_steps = len(train_set)*config['EPOCHS']



warmup_steps = int(0.05*total_steps)

schedule = WarmUpCosineDecay(start_lr=0.0, target_lr=1e-3, warmup_steps=warmup_steps, total_steps=total_steps, hold=warmup_steps)

Và quy trình đào tạo chỉ khác ở chỗ chúng tôi đặt LR của trình tối ưu hóa thành schedule:

model = keras.applications.EfficientNetV2B0(weights=None, 
                                            classes=n_classes, 
                                            input_shape=[224, 224, 3])
  
model.compile(loss="sparse_categorical_crossentropy",
                  optimizer=tf.keras.optimizers.Adam(learning_rate=schedule),
                  jit_compile=True,
                  metrics=['accuracy'])

history3 = model.fit(train_set,
                    epochs = config['EPOCHS'],
                    validation_data=valid_set)

Nếu bạn muốn lưu mô hình, WarmupCosineDecay lịch trình sẽ phải ghi đè lên get_config() phương pháp:

    def get_config(self):
        config = {
          'start_lr': self.start_lr,
          'target_lr': self.target_lr,
          'warmup_steps': self.warmup_steps,
          'total_steps': self.total_steps,
          'hold': self.hold
        }
        return config

Cuối cùng, khi tải mô hình, bạn sẽ phải vượt qua một WarmupCosineDecay như một đối tượng tùy chỉnh:

model = keras.models.load_model('weights.h5', 
                                custom_objects={'WarmupCosineDecay', WarmupCosineDecay})

Kết luận

Trong hướng dẫn này, chúng ta đã xem xét trực giác đằng sau Khởi động tốc độ học tập – một kỹ thuật phổ biến để điều khiển tốc độ học tập trong khi đào tạo mạng lưới thần kinh.

Chúng tôi đã triển khai quá trình khởi động tốc độ học tập bằng phân rã cosine, loại giảm LR phổ biến nhất kết hợp với khởi động. Bạn có thể triển khai bất kỳ chức năng nào khác để giảm bớt hoặc không hề giảm tốc độ học tập – để việc đó cho các cuộc gọi lại khác, chẳng hạn như ReduceLROnPlateau(). Chúng tôi đã triển khai khởi động tốc độ học tập dưới dạng Lệnh gọi lại Keras, cũng như Lịch trình tối ưu hóa Keras và lập biểu đồ về tốc độ học tập qua các kỷ nguyên.

Dấu thời gian:

Thêm từ xếp chồng lên nhau