通过联合学习PySyft和Pygrid来预测涡轮风扇发动机的维护

27 篇文章 7 订阅
11 篇文章 12 订阅
无需直接访问数据就能从机器学习的奇迹中受益吗?如今,机器学习可用于准确预测和预防发动机故障。但是,如果不允许访问传感器数据,如何防止昂贵,重要的机械故障呢?

机器学习在行业中变得越来越重要,例如通常用于降低成本和提高效率,或者专门用于预测性维护。预测性维护是确定设备状况的一种做法,以便估计何时应该执行维护-不仅可以预防灾难性故障,还可以避免不必要的维护,从而节省了时间和金钱。但是在许多情况下,根本没有足够的数据来执行预测-否则,将不允许(或不可能)访问数据。这可能是由于数据保密合规性/法律原因(在医疗用例中通常如此),或者仅仅是由于互联网连接质量差或数据量大,访问和传输数据非常昂贵或复杂。

本文将展示如何使用PySyft(用于安全私有机器学习的OpenMined开源库)实现联合学习,以在边缘设备上训练机器学习模型而无需集中数据。在这里,我们演示了概念证明(POC),可通过不断提高对飞机燃气涡轮发动机的剩余寿命的预测来防止机器故障。

您可以签出完整的代码并自己运行POC:https://github.com/matthiaslau/Turbofan-Federated-Learning-POC。
用例
假设我们是飞机燃气涡轮发动机的制造商,我们出售它们。我们的引擎真的很棒,我想说是市场上最好的,但是仍然可能会不时出现故障。对于我们的客户,运营公司而言,故障和随后的停运是非常昂贵的。而且,如果我们过早地无故维护它们,那么也会有中断,这会花费很多钱。简而言之,及时维护引擎可以为我们的客户节省很多钱。在这一概念验证中,我们将预测发动机的剩余使用寿命(RUL),并在我们认为会发生故障之前将其转换为维护周期。

 

由于我们无法直接访问引擎的运行数据,因此运营公司将这些数据视为机密信息,这使该任务更加艰巨。当然,我们仍然希望提供所描述的故障预警系统。

内部涡轮风扇引擎提供了一些数据,这些数据将用于训练初始机器学习模型。市场上的所有发动机都将通过读取发动机传感器测量值中的软件组件进行扩展,使用该模型预测RUL,并对低RUL做出反应并执行维护。在维护过程中,维护人员将估算发动机的剩余寿命,以创建数据标签。然后,将使用包含该标签的完整数据系列来定期重新训练模型,以随着时间的推移提高预测质量。

联合学习
联合学习的主要思想是,在不暴露或传输其数据的情况下,跨拥有本地数据的多个分散边缘节点训练机器学习模型。

您可以在此免费的在线课程“ Udacity上的安全和私有AI”中了解有关此主题和PySyft基础知识的更多信息。

数据
对于引擎仿真,使用NASA [1]的“ Turbofan引擎降级仿真数据集”。

涡扇发动机的简化图[2]

 

NASA数据集包含使用C-MAPSS(商用模块化航空推进系统仿真)进行仿真的发动机退化数据。在不同的运行条件和故障模式组合下模拟了四个不同的集合。每一套包括针对多个发动机及其使用寿命的每个周期的运行设置和传感器测量值(温度,压力,风扇速度等)。有关数据的更多信息,请参见[2]

项目先决条件 引擎的数据序列以失败而告终,因此我们无法按原样使用它们来模拟在维护/失败后仍将继续运行的引擎。为了模拟我们的涡扇发动机,我们将数据集中的多个发动机数据序列组合到每个发动机节点的一组数据中。然后由引擎节点按顺序重播这些系列。

当应该进行维护时,发动机将设置为维护模式,但仿真将继续找出理论故障时刻。这取代了维护人员的估计,使我们能够评估预测的成功。

为了为我们的POC准备数据,需要将其下载并拆分。我们将使用“ FD001”套件,其中包含100个用于训练的引擎和100个用于验证/测试的引擎。火车数据被分为一个子集进行初始训练(5个引擎系列)和5个子集用于我们的每个引擎节点(每个引擎节点19个引擎系列)。测试数据分为一个子集用于验证(50个发动机系列)和一个子集用于测试(50个发动机系列)。

data_preprocessor脚本为我们完成了所有这一切(https://github.com/matthiaslau/Turbofan-Federated-Learning-POC/blob/master/data_preprocessor.py)。自己执行此操作时,请确保已安装所有要求。

python data_preprocessor.py --turbofan_dataset_id=FD001 --engine_percentage_initial=5 --engine_percentage_val=

 

 

数据分析 现在该项目正式开始! 第一步是分析作为制造商的我们拥有的初始数据,以了解有关数据本身的更多信息。由于这不是本文的重点,因此我们将使分析简短,请在数据分析笔记本中查看更多详细信息。

在绘制了所有发动机的所有传感器数据后,我们可以清楚地看到一些传感器出现故障的模式,这很重要,因为这意味着我们的回归模型很有可能会运行

 

                                                     所有发动机故障的传感器数据测量

 

 

为了配置我们的POC,了解引擎运行的周期数量也很有帮助,因此让我们对其进行绘制。

我们小样本集中的引擎运行约200到300个循环。

初始模型
下一步是准备训练数据并设计模型。然后训练,评估并保存初始模型,以供我们的引擎进一步使用。

有关完整示例,请参阅初始培训笔记本。

数据准备
在从初始引擎以及用于验证和测试的引擎读取数据文件后,首先要做的是为训练数据中的每个数据行计算RUL。
# retrieve the max cycles per engine: RUL
train_rul = pd.DataFrame(train_data.groupby('engine_no')['time_in_cycles'].max()).reset_index()

# merge the RULs into the training data
train_rul.columns = ['engine_no', 'max']
train_data = train_data.merge(train_rul, on=['engine_no'], how='left')

# add the current RUL for every cycle
train_data['RUL'] = train_data['max'] - train_data['time_in_cycles']
train_data.drop('max', axis=1, inplace=True)

 

 

然后,我们选择在数据分析期间被识别为相关的列,主要是删除空的和恒定的传感器测量值。

为了进行模型训练,来自引擎的数据被划分为滚动窗口,因此它具有维度(行总数,每个窗口的时间步长,特征列)。

对于引擎,窗口大小为3时,看起来像这样: [1 2 3 4 5 6]-> [1 2 3],[2 3 4],[3 4 5],[4 5 6]

作为每个窗口的训练标签,选择窗口序列中最后一个值的RUL计算值。

通过将数据拆分为多个窗口,将删除小于窗口大小的数据样本。这尤其意味着我们无法为较小的发动机数据时间序列预测RUL值。一种替代方法是填充序列,以便我们可以使用更短的序列-但是我们可以忽略较小的序列,因为重要的部分是在引擎接近故障且所有引擎的运行时间都超过我们希望的窗口大小时正确预测。

将窗口大小设置为80可提供570个训练样本,2987个样本进行验证和2596个样本进行测试。请记住:我们想进行联合学习,因此初次训练只需要很少的数据是可以的,我们不应该使用验证/测试中的更多数据来进行训练,因为稍后在我们使用更多数据进行训练时将需要此数据。 。

如前所述,由于系统的降级通常可以忽略不计,直到经过一段时间的运行,因此早期和较高的RUL值可能是不合理的。我们可以通过削减RUL值来解决这个问题。这意味着我们可以很好地使用我们的模型来正确地预测RUL值,而不要超过定义的阈值(例如110)。
# clip RUL values
rul_clip_limit = 110

y_train = y_train.clip(max=rul_clip_limit)
y_val = y_val.clip(max=rul_clip_limit)
y_test = y_test.clip(max=rul_clip_limit)

 

 

这使得模型将具有较高RUL值的样本视为相等,并提高了模型稳定性。

 

该模型 在设计用于联合学习的模型时,请务必记住,这些模型将在边缘设备上进行训练,并且取决于特定的联合学习设置,在包含多个设备的培训回合中可能会有很多通信开销。当我们为涡轮风扇引擎部署软件时,我们不能指望能够访问许多资源,例如GPU。如果可能的话,使用简单的模型总是有帮助的,但在这里它甚至更重要!

香草LSTM是解决此问题的有趣方法,但是为了简单起见,在这里我们将从纯稠密模型开始:

class TurbofanModel(nn.Module):
    def __init__(self, input_size):
        super().__init__()

        self.fc1 = nn.Linear(input_size, 24)
        self.fc2 = nn.Linear(24, 24)
        self.fc3 = nn.Linear(24, 24)
        self.fc4 = nn.Linear(24, 1)
        
    def forward(self, x):
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = F.relu(self.fc3(x))
        x = self.fc4(x)
        # reshape to target size
        x = x[:, -1, :]
        
        return x
初步训练
我们已经准备好数据,有了模型,现在我们只是进行定期训练,然后观察损失的减少。我们最终得到了一个模型,对于我们使用的少量数据而言,该模型的性能还不错。

 

网格生态系统 从整个基础结构的角度来看,我们需要一个设置,在其中可以执行联合学习回合的典型阶段:

1.Selection

引擎节点可以注册可用于联合训练的新本地数据

中央“培训师”可以查找可用数据以进行联合训练

选择所有拥有可用于训练的新数据的引擎

2.配置

当前模型可以发送到引擎节点进行训练

可以将有关如何执行训练的进一步配置传达给引擎

3.训练

中央“训练者”可以开始对引擎进行训练

引擎使用本地数据进行训练

4.报告

引擎节点可以将更新后的模型发送回“训练者” “训练者”平均所有模型以创建新的改进的全局模型

 

此外,需要部署模型,以便引擎可以将其用于常规预测。

PyGrid进行补救!这正是PyGrid设计的目的,它是一个由数据所有者和数据科学家组成的对等网络,可以使用PySyft共同训练AI模型。该项目目前正在不断发展和完善,因此请留意当前的文档。

在此POC中,我们使用了不需要将训练配置传输到引擎节点的策略,而是利用了PySyft的指针策略。这意味着训练者组件正在定义和执行所有培训逻辑,并且PySyft会通过指针将必要的命令自动传达给引擎节点。随着PySyft和PyGrid的发展,本文将更新为其他完全分离的策略。
联合学习回合
让我们回顾一下实施过程。当引擎启动时,它们将随着时间的推移开始仿真传感器数据,并且当它们的寿命长于窗口大小时,它们将开始使用来自网格的初始模型来预测RUL。失败后,从开始到失败的整个数据序列将记录在网格中,并且当网格中有足够的可用数据时,培训师将开始新的联合培训回合。改进后的模型随后将部署到网格中,以直接改善引擎的预测,然后一切重新开始。

1.预测RUL
2.注册本地引擎数据
3.在网格中搜索数据
4.联合培训
5.部署模型

 

预测RUL

我们的网格始终为引擎提供当前的基本模型,以预测RUL。对于此用例,引擎应下载模型,然后使用本地传感器数据在本地执行预测。由于当前正在PyGrid中重写从网格下载的模型,因此我们现在使用远程预测:

def predict_rul(data):
    """ Predict the RUL for the given data.

    :param data: The data to predict the RUL for
    :return: The predicted RUL
    """
    # prepare the last data window for inference
    sensor_data_inference = prepare_data_for_prediction(data)

    # predict the current RUL
    my_grid = PublicGridNetwork(
    	hook,
        "http://{}".format(config.grid_gateway_address)
    )

    prediction = None
    try:
        prediction = int(
        	my_grid.run_remote_inference(MODEL_ID, sensor_data_inference)
        )
    except RuntimeError:
        print('Model "{}" does not exist.'.format(MODEL_ID))
        pass

    return prediction

 

注册本地引擎数据
要了解引擎注册新数据时发生的情况,有助于了解如何在网格中搜索数据是有帮助的。在网格网关中搜索带有特定标签的数据时,网格会询问每个注册的网格节点是否拥有这些标签的数据。因此,有关可用数据的知识位于网格节点上,而网格节点在我们的POC中运行在引擎上。因此,引擎将其新数据发送到本地网格节点,而不是网格网关本身。
# tag the data so it can be searched within the grid
tensor_x_train_new = tensor_x_train_new.tag("#X", "#turbofan", "#dataset").describe("The input datapoints to the turbofan dataset.")
tensor_y_train_new = tensor_y_train_new.tag("#Y", "#turbofan", "#dataset").describe("The input labels to the turbofan dataset.")

# send the data to the local grid node
grid_node = NodeClient(hook, address="ws://{}".format('localhost:8001'))
shared_data.append(tensor_x_train_new.send(grid_node))
shared_labels.append(tensor_y_train_new.send(grid_node))

 

 

在网格中搜索数据 现在,培训师组件可以轻松地向我们的网格网关询问数据:

grid = PublicGridNetwork(hook, "http://{}".format(config.grid_gateway_address))
inputs = grid.search("#X", "#turbofan", "#dataset")
labels = grid.search("#Y", "#turbofan", "#dataset")

 

结果,我们收到指向数据的指针,而不是数据本身。因此,无需传输数据,我们现在就可以像实际数据一样利用PySyft魔术在训练中使用这些指针。

联合训练
培训师会定期检查网格中是否有新数据,并等待直到有足够的数据,然后开始联合培训。培训本身看起来仍然很熟悉,但是增强了一些细节。
def train(model, data, labels, criterion):
    """ Train the model with the given data in a federated way.

    :param model: Model to train
    :param data: Data to use for training
    :param labels: Labels to use for loss calculation
    :param criterion: Criterion to use for loss calculations
    :return: Loss of this epoch
    """
    model.train()

    # determine the total amount of data samples in this epoch
    epoch_total = data_result_size(data)

    running_loss = 0

    for i in range(len(data)):
        # initialize an dedicated optimizer for every worker to prevent errors with adams momentum
        optimizer = optim.Adam(model.parameters())

        for j in range(len(data[i])):
            # check the location of the data and send the model there
            worker = data[i][j].location
            model.send(worker)

            # train one step
            optimizer.zero_grad()
            output = model(data[i][j])

            loss = criterion(output, labels[i][j])
            loss.backward()
            optimizer.step()

            # get the updated model and the loss back from the worker
            model.get()
            loss = loss.get()

            running_loss += loss.item() * data[i][j].shape[0]

    epoch_loss = running_loss / epoch_total

    return epoch_loss
首先要提到的是,为每个受过训练的引擎/工人创建了专用的优化器。这是因为使用了Adam优化器,而Adam在后台使用了Momentum。由于Momentum会累积过去步骤的梯度,并且这些梯度可能会存在于其他引擎上,因此使用单个优化程序将失败。对于我们的用例,Adam仍然是一个非常好的优化器,这就是每个引擎只有一个优化器的原因。另一种方法是使用SGD。

您注意到的第二件事是模型需要移至数据。好的,这很明显,我们希望将数据保留在引擎上。因此,在每个训练步骤中,都会查找数据的位置并将模型发送到那里,仅保留指向该模型的指针。然后,在优化程序步骤之后,从引擎取回更新的模型和损失。仅此而已,PySyft正在处理所有通信和命令委派。

部署模型
最后要做的是将新模型部署到网格,以便引擎可以直接开始使用它。
def serve_model(model):
    """ Serve the model to the grid.

    :param model: Model to serve
    """
    trace_model = torch.jit.trace(model, torch.rand((1, WINDOW_SIZE, 11)))

    grid = PublicGridNetwork(hook, "http://{}".format(config.grid_gateway_address))

    # note: the current implementation only returns the first node found
    node = grid.query_model_hosts(MODEL_ID)
    if node:
        # the model was already deployed, delete it before serving
        node.delete_model(MODEL_ID)
        node.serve_model(trace_model, model_id=MODEL_ID, allow_remote_inference=True)
    else:
        grid.serve_model(trace_model, id=MODEL_ID, allow_remote_inference=True)
使用jit对模型进行序列化,然后向网格网关询问当前托管模型的节点。因此,不是网格网关本身托管我们的模型,而是网格的一个随机节点。从该节点删除该模型,并部署新版本。

现在,通过使用引擎上的新数据对模型进行了改进,将其重新部署到网格中,培训师可以继续等待足够的新数据以开始下一轮训练。
我们了解了它的工作原理,现在让我们来看看它的作用! 🏁

在项目docker-compose.yml中使用docker准备了完整的设置。它包含了:
Jupyter笔记本的容器
PyGrid网格网关
5引擎
联合培训师
引擎容器由自定义引擎节点和PyGrid网格节点组成。引擎节点正在读取传感器数据,使用网格中的当前模型来控制引擎状态并预测RUL。联盟教练定期检查网格中是否有足够的新数据,然后开始新的联盟学习回合。回合完成后,新模型将被提供给网格,以供引擎节点直接使用。
docker-compose up -d 

引擎节点公开一个界面,该界面显示引擎状态,统计信息和传感器值:localhost:800 [1-5]。您还可以签出网格节点的接口:localhost:300 [1-5]。

 

 

还可以浏览联合训练的日志,以查看联合训练的实际应用:

docker logs -f trainer

docker-compose.yml中有很多参数,为获得理想的结果,您需要对其进行一些调整。以下是最重要的说明:

CYCLE_LENGTH(发动机):一个发动机循环将花费的秒数。减少以加速引擎仿真,增加以给培训师足够的时间来完成联合培训。
NEW_DATA_THRESHOLD(训练者):联合训练者将在开始新一轮培训之前等待此新数据量。增加以防止训练回合数据太少。
EPOCHS(训练者):联合培训师用于训练的时期数。

 

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值