如何把deepseek-R1微调/蒸馏为某领域的一个专家?

发布时间:
2025-02-15 20:33
阅读量:
3

1. 研究背景

DeepSeek-R1里面有个核心的环节是蒸馏,也就是把大模型的知识蒸馏到小模型里面。知识蒸馏是一种有效的模型压缩技术,能够在保持较高性能的同时,显著减少模型的复杂度和计算需求。但是网上现在关于蒸馏的文章太杂,而且很多观点来源不明确,所以本文以知识蒸馏综述这篇论文为基础,摘取出知识蒸馏的方案,并且把最新的知识蒸馏技术、以及DeepSeek-R1的知识蒸馏做了介绍。本文全面回顾了知识蒸馏的相关研究,从知识类型、蒸馏方案、教师-学生架构、蒸馏算法、性能比较和应用等多个方面进行了详细的分析和讨论。研究表明,知识蒸馏是一种有效的模型压缩技术,能够在保持较高性能的同时,显著减少模型的复杂度和计算需求。

论文名称: Knowledge Distillation: A Survey

论文地址:

arxiv.org/pdf/2006.0552
  1. 研究问题:这篇文章要解决的问题是如何有效地将一个大型深度神经网络(教师模型)的知识转移到一个小模型(学生模型),以便在计算资源有限的设备上部署这些模型。
  2. 研究难点:该问题的研究难点包括:如何从大型教师模型中提取丰富的知识,以及如何将这些知识有效地转移到学生模型中。

Table1 知识蒸馏的全过程


2. 知识类型

知识蒸馏中的知识可以分为基于响应的知识、基于特征的知识以及基于关系的知识。

2.1 基于响应的知识

基于响应的知识,具体是指教师模型最后一个输出层产生的神经响应。这种知识的核心价值在于,它能够直接模仿教师模型的最终预测结果。其实现原理是基于这样一个事实:在深度模型中,最后一个全连接层输出的 logits 向量 z,蕴含了模型对输入数据的关键判断信息。基于响应的知识蒸馏方法,具有显著的优势 —— 简单且高效。这一特性使其在模型压缩任务中表现卓越,能够在不显著损失模型性能的前提下,有效减少模型的参数数量和计算复杂度,其计算公式如下:

基于响应的知识

其中, ​ 和 ​ 分别是教师和学生模型的输出, ​ 表示逻辑损失函数。

Table2 基于响应的知识

2.2 基于特征的知识

使用中间层的特征表示来指导学生模型的训练。深度神经网络擅长学习具有递增抽象性的多层次特征表示。这被称为表征学习(Bengio等人,2013年)。因此 中间层的输出,即特征图,也可以作为知识来监督学生模型的训练。具体来说,来自中间层的基于特征的知识和基于响应的知识一样好,尤其对于训练更薄、更深的网络。

公式如下:

基于特征的知识

其中,中, 分别是教师模型和学生模型中间层的特征图。当教师模型和学生模型的特征图形状不同时,通常会应用转换函数()和( )。表示用于匹配教师模型和学生模型特征图的相似性函数。其实可以用各种常见的范数表示,譬如 距离, 距离, 交叉熵损失等,这个可以根据实际的情况进行选择。

Table3 基于通用特征的知识


2.3 基于关系的知识

基于响应和基于特征的知识都使用了教师模型中特定层的输出。基于关系的知识进一步探索不同层或数据样本之间的关系。探索不同数据样本之间的关系。这种知识类型探索不同数据样本之间的关系。具体来说,基于关系的知识蒸馏方法通过最小化教师模型和学生模型特征对之间相似性损失函数来实现知识转移,公式如下:

基于关系的知识

其中 ft 和 fs 分别是教师和学生模型的特征图。从教师模型中选择特征图对 ˆft 和 ˇft,从学生模型中选择特征图对 ˆfs 和 ˇfs。Ψt(.) 和 Ψs(.) 是教师和学生模型特征图对的相似度函数。 表示教师和学生特征图之间的相关函数,当然这个 也是可以改变的, 距离, ,KL散度,||.||F, 等方式都可以进行表示。Table1里面的蒸馏损失给出了各种可行的方案。

Table4 基于关系的知识

Table1 基于关系的知识

2.4 模型蒸馏python代码

import torch import torch.nn as nn import torch.optim as optim # 定义教师模型(这里以简单的多层感知机为例) class TeacherModel(nn.Module): def __init__(self, input_size, hidden_size, output_size): super(TeacherModel, self).__init__() self.fc1 = nn.Linear(input_size, hidden_size) self.relu = nn.ReLU() self.fc2 = nn.Linear(hidden_size, output_size) def forward(self, x): out = self.fc1(x) out = self.relu(out) out = self.fc2(out) return out # 定义学生模型(结构比教师模型简单) class StudentModel(nn.Module): def __init__(self, input_size, hidden_size, output_size): super(StudentModel, self).__init__() self.fc1 = nn.Linear(input_size, hidden_size // 2) self.relu = nn.ReLU() self.fc2 = nn.Linear(hidden_size // 2, output_size) def forward(self, x): out = self.fc1(x) out = self.relu(out) out = self.fc2(out) return out # 超参数 input_size = 784 teacher_hidden_size = 512 student_hidden_size = 256 output_size = 10 epochs = 10 learning_rate = 0.001 temperature = 2.0 alpha = 0.7 # 创建教师模型和学生模型实例 teacher_model = TeacherModel(input_size, teacher_hidden_size, output_size) student_model = StudentModel(input_size, student_hidden_size, output_size) # 假设已经有教师模型的预训练权重(这里省略加载过程) # 如果没有预训练权重,需要先对教师模型进行训练 teacher_model.eval() # 损失函数(包含知识蒸馏损失和分类损失) criterion_kd = nn.KLDivLoss(reduction='batchmean') criterion_ce = nn.CrossEntropyLoss() # 优化器 optimizer = optim.Adam(student_model.parameters(), lr=learning_rate) # 模拟输入数据(这里使用随机数据代替真实数据) inputs = torch.randn(64, input_size) labels = torch.randint(0, output_size, (64,)) for epoch in range(epochs): optimizer.zero_grad() # 教师模型的输出 with torch.no_grad(): teacher_outputs = teacher_model(inputs) teacher_outputs_softmax = nn.Softmax(dim=1)(teacher_outputs / temperature) # 学生模型的输出 student_outputs = student_model(inputs) student_outputs_softmax = nn.Softmax(dim=1)(student_outputs / temperature) # 计算知识蒸馏损失 loss_kd = criterion_kd(torch.log(student_outputs_softmax), teacher_outputs_softmax) # 计算分类损失 loss_ce = criterion_ce(student_outputs, labels) # 总损失,这里使用的是最简单的alpha作为超参数 loss = alpha * loss_kd + (1 - alpha) * loss_ce loss.backward() optimizer.step() print(f'Epoch {epoch + 1}/{epochs}, Loss: {loss.item()}')

3. 蒸馏方案

根据教师模型和学生模型是否同时更新,知识蒸馏可以分为离线蒸馏、在线蒸馏和自蒸馏。

3.1 离线蒸馏

从预训练的教师模型中提取知识,然后将其用于指导学生模型的训练。离线蒸馏方法通常采用单向知识传递和两阶段训练程序。然而,具有巨大训练时间的复杂高容量教师模型是不可避免的,而在教师模型的指导下,学生模型在离线蒸馏中的训练通常效率较高。此外,大型教师模型与小型学生模型之间的能力差距始终存在,学生往往在很大程度上依赖教师。离线蒸馏是最常见的知识蒸馏方法,通常用于从预训练的教师模型中提取知识,然后将其用于指导学生模型的训练。具体步骤包括:首先,在大量训练数据上预训练一个复杂的教师模型;然后,使用教师模型的输出(如logits或中间特征)来指导学生模型的训练。这种方法的优势在于简单且易于实现,但缺点是教师模型和学生模型的结构和容量可能存在较大差异,导致学生模型过度依赖教师模型。

3.2 在线蒸馏

教师模型和学生模型同时更新,整个知识蒸馏框架端到端可训练。在线蒸馏是一种端到端的训练方法,教师模型和学生模型同时更新。具体步骤包括:在训练过程中,教师模型和学生模型都参与前向传播和反向传播,知识通过模型的参数更新过程从教师模型传递到学生模型这种方法的优势在于能够实时更新模型,适用于没有大容量高性能教师模型的场景,但缺点是计算复杂度较高具体来说,在深度相互学习中,多个神经网络以协作的方式进行工作。在训练过程中,任何一个网络都可以作为学生模型,其他模型则可以作为教师模型。

3.3 自蒸馏

自蒸馏是一种特殊的在线蒸馏方法,同一网络被用作教师和学生模型,通常是将深层网络的知识蒸馏到浅层网络。具体步骤包括:训练一个深层网络作为教师模型,然后使用该模型的中间层或晚期层的输出来指导一个浅层网络(学生模型)的训练。这种方法的优势在于能够有效地利用深层网络的知识和减少模型的复杂性,但缺点是可能会导致浅层网络过拟合。



图5 不同的蒸馏方案。“预训练”的红色表示网络在蒸馏之前已经学习,“待训练”的黄色表示网络在蒸馏过程中学习。


4. 教师-学生模型架构

教师模型和学生模型的设计对知识蒸馏的性能有重要影响。通常,学生模型是教师模型的简化版本,具有更少的层和通道数,或者是在量化后的版本。

图6 教师-学生模型架构


5. 更复杂的蒸馏方案

5.1 对抗性蒸馏

对抗性蒸馏(Adversarial Distillation)是一种通过引入对抗学习技术来提高学生模型性能的方法。这种方法借鉴了生成对抗网络(GANs)的思想,利用生成器和判别器之间的博弈来提升模型的泛化能力。图7 主要对抗性蒸馏方法的各个类别。(a) 在生成对抗网络(GAN)中的生成器生成训练数据以提高知识蒸馏(KD)性能;教师模型可能被用作判别器。(b) 在GAN中的判别器确保学生模型(也作为生成器)模仿教师模型。(c) 教师和学生共同构成一个生成器;通过判别器增强在线知识蒸馏。其中在下面这个公式中,Ft(.) 和 Fs(.) 分别是教师模型和学生模型的输出。G(z) 表示由给定随机输入向量z的生成器G生成的训练样本,而LG​是一个蒸馏损失,用于强制预测概率分布与真实概率分布之间的匹配,例如交叉熵损失或Kullback-Leibler(KL)散度损失。

图7 对抗性蒸馏


5.2 多教师蒸馏

不同的教师架构可以为学生网络提供各自有用的知识。多个教师网络可以在训练学生网络的期间分别或整体用于蒸馏。在一个典型的教师-学生框架中,教师通常拥有一个大型模型或一组大型模型的集成。为了从多个教师转移知识,最简单的方法是使用所有教师的平均响应作为监督信号。


图8 多教师蒸馏

其实多教师融合方案又可以通过下面不同的方式进行融合。

(1)简单平均法(Simple Averaging)

使用所有教师模型的输出进行简单平均,将平均结果作为学生模型的监督信号。这种方法简单直观,但可能无法充分利用不同教师模型的独特知识。

(2)特征融合(Feature Fusion):

将多个教师模型的中间层特征进行融合,生成一个综合的特征表示,然后用于指导学生模型。这种方法可以捕捉到不同教师模型的特征信息。

(3)选择性融合(Selective Fusion):

根据每个教师模型的特定能力,选择性地融合其输出。例如,某些教师可能在某些任务上表现更好,可以选择性地使用这些教师的输出。或者说使用两个教师网络,其中一个教师向学生传递基于响应的知识,另一个教师向学生传递基于特征的知识

(4)动态融合(Dynamic Fusion):

在训练过程中动态调整不同教师模型的权重,以适应不同的训练阶段或任务需求。这种方法可以根据任务的复杂性或数据的变化来调整融合策略。


5.3 跨模态蒸馏

跨模态蒸馏(Cross-Modal Distillation)被描述为一种知识转移技术,用于在不同的模态之间传递知识。具体来说,跨模态蒸馏涉及将一个模态(通常是预训练的教师模型)中的知识转移到另一个模态(学生模型)中,以便在缺乏目标模态标签或数据的情况下进行训练。文章中提到了一些典型的跨模态蒸馏方法,包括使用对齐的成对样本进行知识转移。例如,从RGB图像中提取的特征可以用于指导深度图像的训练。还有一些方法利用生成对抗网络(GANs)来生成合成数据,以增强目标模态的训练效果。其实多模态的表现形式太多,但是为了读者能够比较简单地进行理解,所以就使用两个模态蒸馏进行表示。此外,跨模态蒸馏面临的一个主要挑战是模态间的差异,特别是当不同模态之间的数据对齐困难时。此外,如何有效地利用不同模态的知识也是一个研究方向。


5.4 基于图的蒸馏

在知识蒸馏领域,基于图(Graph-Based)的蒸馏方法是一种利用图结构来表示和处理数据及其相互关系的技术。这种方法通过构建图模型来捕捉数据之间的复杂关系,并利用这些关系来进行知识转移,图结构用于表示数据样本之间的关系。节点通常代表数据样本,边则表示样本之间的某种关系(如相似性、依赖性等)。在基于图的蒸馏中,知识通常表示为图中的节点特征或边的权重。这些特征和权重包含了丰富的信息,可以用于指导学生模型的学习。


5.5 基于注意力的蒸馏

在知识蒸馏(Knowledge Distillation)中,基于注意力(Attention-Based)的蒸馏方法利用注意力机制来改进学生模型的学习过程。教师模型中的注意力机制用于识别和学习输入数据中的重要特征,这些特征可以是图像中的关键区域、文本中的重要词汇等。通过注意力机制,教师模型可以将注意力分布作为知识传递给学生模型。这意味着学生模型不仅学习教师模型的输出,还学习教师模型在生成输出时所关注的特征。

5.6 无数据蒸馏

无数据蒸馏(Data-Free Distillation)是一种特殊类型的知识蒸馏方法,其核心思想是在没有额外训练数据的情况下进行模型压缩和知识转移。这种方法通常用于解决数据隐私、安全性和法律合规性问题。无数据蒸馏方法通常通过生成新的数据样本来替代实际的训练数据。这些数据样本是通过教师模型的内部表示生成的,而不是从外部数据集中获取的。该方法依赖于一个预训练的教师模型,该模型已经在一个大型数据集上进行了训练。教师模型的内部表示(如激活值、特征图等)被用来生成新的数据样本。其实不严格来说,无数据蒸馏其实跟零样本学习的思想差不多。

无数据蒸馏

5.7 量化蒸馏


量化蒸馏(Quantized Distillation)是一种结合了知识蒸馏和网络量化的技术,旨在通过减少神经网络的权重精度来降低计算复杂度和存储需求,同时保持或提高模型的性能。这种方法特别适用于需要在资源受限的设备上进行部署的应用,如移动设备和嵌入式系统。其实量化蒸馏现在在大模型上得到了广泛使用,量化是指将神经网络中的权重和激活值从高精度(如32位浮点数)转换为低精度(如2位、4位或8位整数)。这种转换可以显著减少模型的存储需求和计算量。譬如DeepSeek-V3里面的FP8就是量化的一个典型例子。在量化蒸馏中,知识蒸馏用于帮助学生模型(通常是量化后的模型)学习教师模型(通常是高精度的模型)的知识。通过模仿教师模型的行为,学生模型可以更好地适应低精度环境。一些量化蒸馏方法采用自适应量化策略,允许在训练过程中动态调整量化级别,以平衡精度和效率。


6. DeepSeek-R1是如何做蒸馏的

DeepSeek-AI 运用蒸馏技术,将大型 “教师” 模型 DeepSeek-R1 的知识,巧妙地转移到较小的 “学生” 模型之中。这一过程的核心目标,是把 DeepSeek-R1 卓越的推理能力融入到更便于管理的模型里,从而拓展其应用领域。
具体而言,整个过程分为以下几个关键步骤:

  1. 数据生成:利用 DeepSeek-R1 生成了高达 80 万条的高质量训练数据。这些数据覆盖范围广泛,涵盖了数学推理(例如 MATH-500 这类典型数学问题)、代码生成以及科学问答等多种实际应用场景。尤为重要的是,这些数据不仅包含了问题的答案,还蕴含着多专家协作下形成的决策逻辑,为后续模型学习提供了丰富而优质的素材。这一块可以看做成是数据蒸馏与模型蒸馏的结合。
  2. 基线模型选择:精心挑选了多个开源的小型密集模型作为基线模型,其中包括 Qwen2.5 系列和 Llama3 系列的模型。像 Qwen2.5-Math-1.5B、7B、14B、32B,Qwen2.5-14B、32B,以及 Llama-3.1-8B 和 Llama-3.3-70B-Instruct 等。这些模型各有特点,为蒸馏实验提供了多样化的基础。
  3. 蒸馏与微调在蒸馏阶段,对这些小型模型仅进行简单的监督微调(SFT),而不涉及强化学习(RL)阶段。这样做主要是为了突出展示蒸馏技术本身的有效性,而将强化学习阶段的探索留给更广泛的研究领域。通过微调参数,小模型能够直接学习 DeepSeek-R1 的复杂推理模式。例如,在面对数学证明题时,小模型能够像 DeepSeek-R1 一样,自动选择最优的证明路径,而不是盲目地随机尝试,大大提升了问题解决的效率和准确性。

DeepSeek-R1的蒸馏过程涉及多个步骤和技术,旨在将大型模型的知识迁移到小型模型中,以提高其性能和效率。以下是详细的蒸馏过程。

6.1 选择教师模型和学生模型

(1)教师模型

DeepSeek-R1是一个经过大规模强化学习训练的强大推理模型,特别擅长数学、编程等推理任务。选择教师模型时,关键在于其推理能力和知识储备,DeepSeek-R1在这些方面表现出色,所以它被选择为教师模型作为知识传递的来源。

(2)学生模型

学生模型通常是一个结构相对简单的小型神经网络,如Qwen2.5-Math-1.5B、7B、14B、32B,Qwen2.5-14B、32B,Llama-3.1-8B和Llama-3.3-70B-Instruct,参数较少,计算资源需求较低。选择学生模型时,主要考虑其学习能力和计算效率。此外,DeepSeek采用了参数共享和压缩技术,以进一步优化模型的存储和计算效率。通过共享部分参数,学生模型在保持性能的同时,显著减少了参数数量和存储需求。

6.2 构建蒸馏数据集

(1) 数据集选择

蒸馏数据集应包含足够的样本,以覆盖教师模型所擅长的各种任务和场景。Deepseek-R1使用教师模型训练时的原始数据集或其子集作为蒸馏数据集。数据集的质量和多样性直接影响蒸馏效果。使用高质量、多样化的数据集可以确保学生模型学习到更全面和准确的推理模式。利用 DeepSeek-R1 生成了高达 80 万条的高质量训练数据。这些数据覆盖范围广泛,涵盖了数学推理(例如 MATH-500 这类典型数学问题)、代码生成以及科学问答等多种实际应用场景。尤为重要的是,这些数据不仅包含了问题的答案,还蕴含着多专家协作下形成的决策逻辑,为后续模型学习提供了丰富而优质的素材。

(2) 数据预处理

对数据集进行严格筛选和处理,确保蒸馏效果不受数据质量影响。数据预处理包括数据清洗、格式化和增强等步骤,这些步骤可以提高数据的质量和模型的泛化能力。

6.3 蒸馏策略

6.3.1 多维度知识提取与传递

(1) Logits蒸馏

对输出概率分布计算KL散度损失,适合分类、生成任务。Logits蒸馏通过软化教师模型的输出,使其变得更加平滑和具有不确定性,有助于学生模型学习到更丰富的信息。通过计算教师模型和学生模型输出概率分布的KL散度损失,使学生在输出层上模仿教师模型的决策过程。这个其实就是基于响应的结果蒸馏。原理见2.1。

(2)中间层蒸馏

对齐隐藏层输出,使用MSE或余弦相似度损失。中间层蒸馏通过传递教师模型的中间特征,帮助学生模型学习到更抽象和丰富的特征表示。这个其实就是基于特征的蒸馏,原理见2.2。

(3)注意力矩阵蒸馏

匹配注意力权重,适合需要保留语义关系的任务。注意力矩阵蒸馏通过对齐注意力权重,帮助学生模型更好地理解和处理复杂的语义关系。原理见5.5.

6.3.2 动态权重分配策略

DeepSeek R1 引入了元学习(Meta-Learning)技术,实现了动态权重分配,根据学生模型在不同知识维度的学习进展情况,自动调整不同损失项的权重系数。

(1)元网络:这是一个轻量级神经网络,输入学生模型在各知识维度的误差,输出相应的权重系数。例如,当学生模型在中间层特征图的学习上表现较差时,元网络会提高对应损失项的权重,使模型在后续训练中更加关注中间层特征图的学习。

(2)训练阶段划分:将训练过程分为初期、中期和后期,分别侧重不同的学习任务。初期侧重特征图匹配,为后续学习打牢基础;中期加强注意力权重学习,提升模型对关键信息的捕捉能力;后期聚焦输出层优化,确保模型最终输出结果的准确性。

6.3.3 渐进式蒸馏方法

DeepSeek R1 采用三阶段渐进策略,逐步突破学习瓶颈,提升模型性能。注意哦,这个是DeepSeek-R1蒸馏的三个阶段。不是DeepSeek R1 采用的四阶段训练策略。

(1)预热阶段:使用教师模型生成大量伪标签数据,扩展训练集。教师模型对未标注数据进行推理,输出软标签和中间特征,将这些伪标签数据与真实数据结合,可以极大地扩展训练集规模,提升模型的泛化能力。


(2)对抗训练阶段:引入判别器网络,学生模型试图欺骗判别器,使其认为自己的特征分布与教师模型一致。学生模型的损失函数包括任务损失和对抗损失,通过对抗训练,学生模型能够不断优化特征表达,使其更接近教师模型。对抗性蒸馏(Adversarial Distillation)是一种通过引入对抗学习技术来提高学生模型性能的方法。这种方法借鉴了生成对抗网络(GANs)的思想,利用生成器和判别器之间的博弈来提升模型的泛化能力。图7 主要对抗性蒸馏方法的各个类别。(a) 在生成对抗网络(GAN)中的生成器生成训练数据以提高知识蒸馏(KD)性能;教师模型可能被用作判别器。(b) 在GAN中的判别器确保学生模型(也作为生成器)模仿教师模型。(c) 教师和学生共同构成一个生成器;通过判别器增强在线知识蒸馏。这个其实就是5.1讲的对抗性蒸馏的方式。

(3)自蒸馏增强阶段:学生模型对自身预测结果进行再学习,用预测结果作为新标签进行二次训练,强化知识内化,减少对教师模型的依赖,进一步挖掘自身潜力。这个其实就是3.3自蒸馏的思想。


6.4 损失函数组合

(1)典型权重分配

total_loss = 0.7 * L_task + 0.2 * L_kl + 0.1 * L_hidden其中L_task是学生模型自身任务损失,L_kl是教师-学生的输出分布KL散度,L_hidden是中间层特征损失。通过组合不同类型的损失函数,可以全面优化学生模型的性能,使其在任务性能、特征学习和推理能力等方面都能达到较高水平。DeepSeek-R1的蒸馏过程包括选择适当的教师模型和学生模型、构建高质量的蒸馏数据集、采用多种蒸馏策略(如Logits蒸馏、中间层蒸馏和注意力矩阵蒸馏)以及合理组合损失函数。这些步骤和技术共同作用,成功将DeepSeek-R1的强大推理能力迁移到小型模型中。

(2) 温度参数调整

在蒸馏过程中,DeepSeek引入了温度参数来调整软标签的分布。较高的温度参数可以使分布更加平滑,从而帮助学生模型更好地学习教师模型的输出。随着训练的进行,温度参数逐渐降低,以提高蒸馏效果。

(3) 动态学习率调整

为了提高训练效率,DeepSeek采用了动态学习率调整策略。通过根据训练进度和模型性能动态调整学习率,确保了模型在训练过程中的稳定性和收敛速度。

(4) 正则化技术

为了避免过拟合,DeepSeek在训练过程中引入了正则化技术。例如,使用L2正则化项来约束模型的参数,防止模型过于复杂,从而提高模型的泛化能力。

通过这些训练过程和优化方法,DeepSeek的蒸馏模型不仅在性能上接近甚至超越了原始的大型模型,还在计算效率和资源占用方面表现出色,为资源受限场景下的应用提供了强大的支持。

6.5 DeepSeek-R1的蒸馏模型效果总览

蒸馏后的小型模型在推理任务上表现出色。例如,DeepSeek-R1-Distill-Qwen-1.5B在AIME 2024基准测试中的pass@1得分达到28.9%,在MATH-500上达到83.9%,超越了GPT-4o-0513等较大的非推理模型。其他模型如DeepSeek-R1-Distill-Qwen-32B和DeepSeek-R1-Distill-Llama-70B在大多数基准测试中显著超过了OpenAI的o1-mini模型。

END