<jupyter_start><jupyter_text>TimeSequence - Syft Duet - Data Scientist 🥁Contributed by [@Koukyosyumei](https://github.com/Koukyosyumei) PART 1: Connect to a Remote Duet ServerAs the Data Scientist, you want to perform data science on data that is sitting in the Data Owner's Duet server in their Notebook.In order to do this, we must run the code that the Data Owner sends us, which importantly includes their Duet Session ID. The code will look like this, importantly with their real Server ID.```import syft as syduet = sy.duet('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')```This will create a direct connection from my notebook to the remote Duet server. Once the connection is established all traffic is sent directly between the two nodes.Paste the code or Server ID that the Data Owner gives you and run it in the cell below. It will return your Client ID which you must send to the Data Owner to enter into Duet so it can pair your notebooks.<jupyter_code>from typing import Any
from typing import List as TypeList

import matplotlib.pyplot as plt
import numpy as np
import torch
import torch.nn as nn
from syft import SyModule
from syft.core.plan.plan_builder import ROOT_CLIENT, make_plan
import syft as sy

duet = sy.join_duet(loopback=True)
sy.logger.add(sink="./syft_ds.log")<jupyter_output><empty_output><jupyter_text>Checkpoint 0 : Now STOP and run the Data Owner notebook until Checkpoint 1.<jupyter_code>config = {"steps": 15, "dry_run": True}
duet.store.pandas
L = duet.store["L"].get(request_block=True, delete_obj=False)
input = duet.store["input"]
input_size_0 = duet.store["input_size_0"].get(request_block=True, delete_obj=False)
target = duet.store["target"]
test_input = duet.store["test_input"]
test_target = duet.store["test_target"]
train_ptr = duet.store["train"]
remote_torch = ROOT_CLIENT.torch
remote_python = ROOT_CLIENT.python<jupyter_output><empty_output><jupyter_text>The behavior of the model changes in inference time because it has to predict future values. Although the `forward` method in original implementation receives two arguments, input and futures, which indicates the length of future prediction, `SyModule` can accept only one argument. Thus, this notebook defines two separate models for training and prediction.<jupyter_code># model for training


class Sequence(SyModule):
    def __init__(self, batch_size=97, **kwargs: Any) -> None:
        super().__init__(**kwargs)
        self.lstm1 = torch.nn.LSTMCell(1, 51)
        self.lstm2 = torch.nn.LSTMCell(51, 51)
        self.linear = torch.nn.Linear(51, 1)
        self.batch_size = batch_size

    def forward(self, input: Any) -> Any:
        outputs = remote_python.List([])

        h_t = torch.zeros(self.batch_size, 51)
        c_t = torch.zeros(self.batch_size, 51)
        h_t2 = torch.zeros(self.batch_size, 51)
        c_t2 = torch.zeros(self.batch_size, 51)

        state_1 = remote_python.Tuple((h_t, c_t))
        state_2 = remote_python.Tuple((h_t2, c_t2))

        for input_t in input.split(1, dim=1):
            state_1 = self.lstm1(input_t, state_1)
            state_2 = self.lstm2(h_t, state_2)
            output = self.linear(state_2[0])
            outputs.append(output)

        """ eval mode is currently not supported in ModulePointer
        if not self.training:
            for i in range(10):# if we should predict the future
                state_1 = self.lstm1(output, state_1)
                state_2 = self.lstm2(h_t, state_2)
                output = self.linear(state_2[0])
                outputs.append(output)
        """

        outputs = remote_torch.cat(outputs, dim=1)
        return outputs
# model for prediction


class Sequence_Pred(nn.Module):
    def __init__(self):
        super(Sequence_Pred, self).__init__()
        self.lstm1 = nn.LSTMCell(1, 51)
        self.lstm2 = nn.LSTMCell(51, 51)
        self.linear = nn.Linear(51, 1)

    def forward(self, input, future=0):
        outputs = []
        h_t = torch.zeros(input.size(0), 51, dtype=torch.double)
        c_t = torch.zeros(input.size(0), 51, dtype=torch.double)
        h_t2 = torch.zeros(input.size(0), 51, dtype=torch.double)
        c_t2 = torch.zeros(input.size(0), 51, dtype=torch.double)

        for input_t in input.split(1, dim=1):
            h_t, c_t = self.lstm1(input_t, (h_t, c_t))
            h_t2, c_t2 = self.lstm2(h_t, (h_t2, c_t2))
            output = self.linear(h_t2)
            outputs += [output]
        for i in range(future):  # if we should predict the future
            h_t, c_t = self.lstm1(output, (h_t, c_t))
            h_t2, c_t2 = self.lstm2(h_t, (h_t2, c_t2))
            output = self.linear(h_t2)
            outputs += [output]
        outputs = torch.cat(outputs, dim=1)
        return outputs
local_test_input = test_input.get(request_block=True, delete_obj=False)
local_test_target = test_target.get(request_block=True, delete_obj=False)

input_size_1 = local_test_input.shape[1]
future = L

criterion_pred = torch.nn.MSELoss()
model = Sequence(input_size=(input_size_0, L - 1))
model_pred = Sequence_Pred()
model_pred.double()
for i in range(config["steps"]):
    # training with Plan
    res_ptr = train_ptr(input=input, target=target, model=model)
    # download the trained model to local
    model = res_ptr[0].get(request_block=True, delete_obj=False)
    # update the model for prediction
    model_pred.load_state_dict(model.state_dict())

    # begin to predict, no need to track gradient here
    with torch.no_grad():
        pred = model_pred(local_test_input.double(), future=future)
        loss = criterion_pred(pred[:, :-L], local_test_target)
        print("test loss:", loss.item())
        y = pred.detach().numpy()

    # draw the result
    plt.figure(figsize=(30, 10))
    plt.title(
        "Predict future values for time sequences\n(Dashlines are predicted values)",
        fontsize=30,
    )
    plt.xlabel("x", fontsize=20)
    plt.ylabel("y", fontsize=20)
    plt.xticks(fontsize=20)
    plt.yticks(fontsize=20)

    def draw(yi, color):
        plt.plot(np.arange(input_size_1), yi[:input_size_1], color, linewidth=2.0)
        plt.plot(
            np.arange(input_size_1, input_size_1 + future),
            yi[input_size_1:],
            color + ":",
            linewidth=2.0,
        )

    draw(y[0], "r")
    draw(y[1], "g")
    draw(y[2], "b")

    plt.savefig("predict%d.pdf" % i)
    plt.close()

    if config["dry_run"]:
        break<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10944272445820434, 'fraction_numerical': 0.020278637770897834, 'mean_word_length': 3.2534562211981566, 'pattern_counts': {'":': 3, '<': 13, '<?xml version=': 0, '>': 15, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 1, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '654841', 'license': 'Apache-1.1', 'n_tokens_mistral': 2216, 'n_tokens_neox': 2127, 'n_words': 574, 'path': 'packages/syft/examples/duet/time_sequence_prediction/TimeSequence_Syft_Data_Scientist.ipynb', 'repo_name': 'sanggusti/PySyft', 'size': '6460'}