a b/AI-Genomics.ipynb
1
{
2
  "nbformat": 4,
3
  "nbformat_minor": 0,
4
  "metadata": {
5
    "colab": {
6
      "provenance": [],
7
      "include_colab_link": true
8
    },
9
    "kernelspec": {
10
      "name": "python3",
11
      "display_name": "Python 3"
12
    },
13
    "language_info": {
14
      "name": "python"
15
    }
16
  },
17
  "cells": [
18
    {
19
      "cell_type": "markdown",
20
      "metadata": {
21
        "id": "view-in-github",
22
        "colab_type": "text"
23
      },
24
      "source": [
25
        "<a href=\"https://colab.research.google.com/github/anjimenezp/AI-Genomics/blob/main/AI-Genomics.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
26
      ]
27
    },
28
    {
29
      "cell_type": "code",
30
      "source": [
31
        "import random\n",
32
        "import pandas as pd\n",
33
        "\n",
34
        "pd.set_option('display.width', 200)\n",
35
        "\n",
36
        "def extract_data():\n",
37
        "  df = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/molecular-biology/splice-junction-gene-sequences/splice.data', header=None, names=['Class', 'Name', 'Sequence'])\n",
38
        "  return df\n",
39
        "\n",
40
        "print(extract_data().head(10))"
41
      ],
42
      "metadata": {
43
        "colab": {
44
          "base_uri": "https://localhost:8080/"
45
        },
46
        "id": "tn5X-H1iUjJP",
47
        "outputId": "fdde3345-7e4a-4265-f137-7fbca648d85d"
48
      },
49
      "execution_count": null,
50
      "outputs": [
51
        {
52
          "output_type": "stream",
53
          "name": "stdout",
54
          "text": [
55
            "  Class                     Name                                           Sequence\n",
56
            "0    EI         ATRINS-DONOR-521                 CCAGCTGCATCACAGGAGGCCAGCGAGCAGG...\n",
57
            "1    EI         ATRINS-DONOR-905                 AGACCCGCCGGGAGGCGGAGGACCTGCAGGG...\n",
58
            "2    EI         BABAPOE-DONOR-30                 GAGGTGAAGGACGTCCTTCCCCAGGAGCCGG...\n",
59
            "3    EI        BABAPOE-DONOR-867                GGGCTGCGTTGCTGGTCACATTCCTGGCAGGT...\n",
60
            "4    EI       BABAPOE-DONOR-2817               GCTCAGCCCCCAGGTCACCCAGGAACTGACGTG...\n",
61
            "5    EI       CHPIGECA-DONOR-378               CAGACTGGGTGGACAACAAAACCTTCAGCGGTA...\n",
62
            "6    EI       CHPIGECA-DONOR-903               CCTTTGAGGACAGCACCAAGAAGTGTGCAGGTA...\n",
63
            "7    EI      CHPIGECA-DONOR-1313              CCCTCGTGCGGTCCACGACCAAGACCAGCGGTGA...\n",
64
            "8    EI      GCRHBBA1-DONOR-1260              TGGCGACTACGGCGCGGAGGCCCTGGAGAGGTGA...\n",
65
            "9    EI      GCRHBBA1-DONOR-1590              AAGCTGACAGTGGACCCGGTCAACTTCAAGGTGA...\n"
66
          ]
67
        }
68
      ]
69
    },
70
    {
71
      "cell_type": "code",
72
      "execution_count": null,
73
      "metadata": {
74
        "colab": {
75
          "base_uri": "https://localhost:8080/"
76
        },
77
        "id": "o1FOy9XUDfPA",
78
        "outputId": "b3b1368b-6f42-45bd-84a4-6358aa697426"
79
      },
80
      "outputs": [
81
        {
82
          "output_type": "stream",
83
          "name": "stdout",
84
          "text": [
85
            "                                                 seq type\n",
86
            "0                 CCAGCTGCATCACAGGAGGCCAGCGAGCAGG...   EI\n",
87
            "1                 AGACCCGCCGGGAGGCGGAGGACCTGCAGGG...   EI\n",
88
            "2                 GAGGTGAAGGACGTCCTTCCCCAGGAGCCGG...   EI\n",
89
            "3                GGGCTGCGTTGCTGGTCACATTCCTGGCAGGT...   EI\n",
90
            "4               GCTCAGCCCCCAGGTCACCCAGGAACTGACGTG...   EI\n"
91
          ]
92
        }
93
      ],
94
      "source": [
95
        "import random\n",
96
        "import pandas as pd\n",
97
        "\n",
98
        "def generate_simulated_dna_sequence(length, type):\n",
99
        "    \"\"\"Generate a simulated DNA sequence with more complexity.\"\"\"\n",
100
        "    motifs = {\n",
101
        "    'EI': ['GT', 'GTAAGT'],  # Exon-Intron splice sites\n",
102
        "    'IE': ['AG', 'CAGG'],  # Intron-Exon splice sites\n",
103
        "    'N': ['TATA', 'CAAT', 'AAA', 'TTT', 'CCC', 'GGG']  # Regulatory elements and repetitive sequences\n",
104
        "  }\n",
105
        "\n",
106
        "    # def mutate(sequence):\n",
107
        "    #     # Ajustar el número de mutaciones en función de la longitud de la secuencia\n",
108
        "    #     max_mutations = len(sequence) // 4  # Hasta un 25% de la secuencia\n",
109
        "    #     num_mutations = random.randint(1, max(max_mutations, 1))  # Al menos una mutación\n",
110
        "    #     mutation_sites = random.sample(range(len(sequence)), k=num_mutations)\n",
111
        "    #     for i in mutation_sites:\n",
112
        "    #         sequence = sequence[:i] + random.choice('ACGT') + sequence[i+1:]\n",
113
        "    #     return sequence\n",
114
        "\n",
115
        "    # sequence = ''\n",
116
        "    # while len(sequence) < length:\n",
117
        "    #     if type in motifs:\n",
118
        "    #         motif = random.choice(motifs[type]) + ''.join(random.choice('ACGT') for _ in range(3))\n",
119
        "    #         sequence += mutate(motif)\n",
120
        "    #     else:\n",
121
        "    #         sequence += mutate(''.join(random.choice('ACGT') for _ in range(6)))\n",
122
        "    #     sequence = sequence[:length]\n",
123
        "    # return sequence\n",
124
        "\n",
125
        "\n",
126
        "\n",
127
        "\n",
128
        "# def create_simulated_data(num_samples, seq_length):\n",
129
        "#     types = ['EI', 'IE', 'N']\n",
130
        "#     data = {'seq': [], 'type': []}\n",
131
        "#     for _ in range(num_samples):\n",
132
        "#         seq_type = random.choice(types)\n",
133
        "#         seq = generate_simulated_dna_sequence(seq_length, seq_type)\n",
134
        "#         data['seq'].append(seq)\n",
135
        "#         data['type'].append(seq_type)\n",
136
        "#     return pd.DataFrame(data)\n",
137
        "\n",
138
        "real_data = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/molecular-biology/splice-junction-gene-sequences/splice.data', header=None, names=['type', 'Name', 'seq'])\n",
139
        "real_data.drop('Name', axis=1, inplace=True)\n",
140
        "real_data = real_data[['seq', 'type']]\n",
141
        "\n",
142
        "# num_samples = 65536\n",
143
        "seq_length = 60\n",
144
        "# simulated_data = create_simulated_data(num_samples, seq_length)]\n",
145
        "real_data = pd.DataFrame(real_data)\n",
146
        "print(real_data.head())\n"
147
      ]
148
    },
149
    {
150
      "cell_type": "markdown",
151
      "source": [
152
        "**Cuantificando las etiquetas**"
153
      ],
154
      "metadata": {
155
        "id": "viX4pY5uFnzt"
156
      }
157
    },
158
    {
159
      "cell_type": "code",
160
      "source": [
161
        "from sklearn.preprocessing import LabelEncoder\n",
162
        "\n",
163
        "# Crear una instancia de LabelEncoder\n",
164
        "label_encoder = LabelEncoder()\n",
165
        "\n",
166
        "# Añadir una nueva columna al DataFrame con las etiquetas codificadas\n",
167
        "real_data['type_encoded'] = label_encoder.fit_transform(real_data['type'])\n",
168
        "\n",
169
        "# Verificar las primeras filas del DataFrame para confirmar la adición\n",
170
        "print(real_data.head())\n"
171
      ],
172
      "metadata": {
173
        "colab": {
174
          "base_uri": "https://localhost:8080/"
175
        },
176
        "id": "wNkJlGMrFxcJ",
177
        "outputId": "bafd0818-1806-43ce-96a5-d01978510194"
178
      },
179
      "execution_count": null,
180
      "outputs": [
181
        {
182
          "output_type": "stream",
183
          "name": "stdout",
184
          "text": [
185
            "                                                 seq type  type_encoded\n",
186
            "0                 CCAGCTGCATCACAGGAGGCCAGCGAGCAGG...   EI             0\n",
187
            "1                 AGACCCGCCGGGAGGCGGAGGACCTGCAGGG...   EI             0\n",
188
            "2                 GAGGTGAAGGACGTCCTTCCCCAGGAGCCGG...   EI             0\n",
189
            "3                GGGCTGCGTTGCTGGTCACATTCCTGGCAGGT...   EI             0\n",
190
            "4               GCTCAGCCCCCAGGTCACCCAGGAACTGACGTG...   EI             0\n"
191
          ]
192
        }
193
      ]
194
    },
195
    {
196
      "cell_type": "markdown",
197
      "source": [
198
        "**One_hot_encode**"
199
      ],
200
      "metadata": {
201
        "id": "b9Bg9LBFxFCA"
202
      }
203
    },
204
    {
205
      "cell_type": "code",
206
      "source": [
207
        "import torch\n",
208
        "from torch.utils.data import Dataset\n",
209
        "\n",
210
        "def clean_sequence(seq):\n",
211
        "    return seq.replace(' ', '').upper()  # Elimina espacios y convierte a mayúsculas\n",
212
        "\n",
213
        "def one_hot_encode(seq):\n",
214
        "    mapping = {'A': [1, 0, 0, 0], 'C': [0, 1, 0, 0], 'G': [0, 0, 1, 0], 'T': [0, 0, 0, 1], 'N': [0, 0, 0, 0]}\n",
215
        "    return torch.tensor([mapping.get(nucleotide, [0, 0, 0, 0]) for nucleotide in seq], dtype=torch.float32)\n",
216
        "\n",
217
        "class SeqDatasetOHE(Dataset):\n",
218
        "    def __init__(self, dataframe, seq_col, label_col):\n",
219
        "        self.dataframe = dataframe\n",
220
        "        self.seq_col = seq_col\n",
221
        "        self.label_col = label_col\n",
222
        "\n",
223
        "    def __len__(self):\n",
224
        "        return len(self.dataframe)\n",
225
        "\n",
226
        "    def __getitem__(self, idx):\n",
227
        "        seq = self.dataframe.iloc[idx][self.seq_col]\n",
228
        "        seq = clean_sequence(seq)  # Limpia la secuencia\n",
229
        "        label = self.dataframe.iloc[idx][self.label_col]\n",
230
        "        return one_hot_encode(seq), label\n",
231
        "\n",
232
        "\n",
233
        "print(one_hot_encode(\"CACGAT\"))\n"
234
      ],
235
      "metadata": {
236
        "id": "K2DWM8HoxK0o",
237
        "colab": {
238
          "base_uri": "https://localhost:8080/"
239
        },
240
        "outputId": "50b801b4-a356-4ac3-f265-0461dc70b9b2"
241
      },
242
      "execution_count": null,
243
      "outputs": [
244
        {
245
          "output_type": "stream",
246
          "name": "stdout",
247
          "text": [
248
            "tensor([[0., 1., 0., 0.],\n",
249
            "        [1., 0., 0., 0.],\n",
250
            "        [0., 1., 0., 0.],\n",
251
            "        [0., 0., 1., 0.],\n",
252
            "        [1., 0., 0., 0.],\n",
253
            "        [0., 0., 0., 1.]])\n"
254
          ]
255
        }
256
      ]
257
    },
258
    {
259
      "cell_type": "markdown",
260
      "source": [
261
        "**SPLITS DE ENTRENAMIENTO**"
262
      ],
263
      "metadata": {
264
        "id": "Zr8HTpxWEP1G"
265
      }
266
    },
267
    {
268
      "cell_type": "code",
269
      "source": [
270
        "import random\n",
271
        "\n",
272
        "def quick_split(df, split_frac=0.7):\n",
273
        "    \"\"\"Divide el DataFrame en dos conjuntos de manera aleatoria.\n",
274
        "\n",
275
        "    Args:\n",
276
        "    df (DataFrame): DataFrame a dividir.\n",
277
        "    split_frac (float): Fracción del conjunto para entrenamiento.\n",
278
        "\n",
279
        "    Returns:\n",
280
        "    DataFrame: Conjunto de entrenamiento.\n",
281
        "    DataFrame: Conjunto restante.\n",
282
        "    \"\"\"\n",
283
        "    shuffled = df.sample(frac=1).reset_index(drop=True)\n",
284
        "    split_idx = int(len(df) * split_frac)\n",
285
        "    train_df = shuffled[:split_idx]\n",
286
        "    rest_df = shuffled[split_idx:]\n",
287
        "    return train_df, rest_df\n",
288
        "\n",
289
        "# Genera los conjuntos de entrenamiento, validación y prueba\n",
290
        "train_df, remaining_df = quick_split(real_data, split_frac=0.7)\n",
291
        "val_df, test_df = quick_split(remaining_df, split_frac=0.5)\n",
292
        "\n",
293
        "# Verifica los tamaños de los conjuntos\n",
294
        "print(f\"Entrenamiento: {len(train_df)} muestras\")\n",
295
        "print(f\"Validación: {len(val_df)} muestras\")\n",
296
        "print(f\"Prueba: {len(test_df)} muestras\")\n"
297
      ],
298
      "metadata": {
299
        "colab": {
300
          "base_uri": "https://localhost:8080/"
301
        },
302
        "id": "LHIwvf6OEQWV",
303
        "outputId": "38efe95b-6a70-4dc3-e66f-77806b2af6a2"
304
      },
305
      "execution_count": null,
306
      "outputs": [
307
        {
308
          "output_type": "stream",
309
          "name": "stdout",
310
          "text": [
311
            "Entrenamiento: 2233 muestras\n",
312
            "Validación: 478 muestras\n",
313
            "Prueba: 479 muestras\n"
314
          ]
315
        }
316
      ]
317
    },
318
    {
319
      "cell_type": "markdown",
320
      "source": [
321
        "**Preparar DataLoader**"
322
      ],
323
      "metadata": {
324
        "id": "rtlx_n1NE9-B"
325
      }
326
    },
327
    {
328
      "cell_type": "code",
329
      "source": [
330
        "from torch.utils.data import DataLoader\n",
331
        "\n",
332
        "batch_size = 32  # Puedes ajustar esto según las necesidades de tu modelo y hardware\n",
333
        "\n",
334
        "train_dataset = SeqDatasetOHE(train_df, 'seq', 'type_encoded')\n",
335
        "val_dataset = SeqDatasetOHE(val_df, 'seq', 'type_encoded')\n",
336
        "test_dataset = SeqDatasetOHE(test_df, 'seq', 'type_encoded')\n",
337
        "\n",
338
        "train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)\n",
339
        "val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)\n",
340
        "test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)\n"
341
      ],
342
      "metadata": {
343
        "id": "sTWHuU7HE7EN"
344
      },
345
      "execution_count": null,
346
      "outputs": []
347
    },
348
    {
349
      "cell_type": "markdown",
350
      "source": [
351
        "**Modelo CNN**"
352
      ],
353
      "metadata": {
354
        "id": "XN-zlRcXGzu4"
355
      }
356
    },
357
    {
358
      "cell_type": "code",
359
      "source": [
360
        "import torch\n",
361
        "import torch.nn as nn\n",
362
        "\n",
363
        "class DNA_CNN(nn.Module):\n",
364
        "    def __init__(self, seq_len, num_classes):\n",
365
        "        super(DNA_CNN, self).__init__()\n",
366
        "        self.conv1 = nn.Conv1d(4, 32, kernel_size=3, stride=1, padding=1)\n",
367
        "        self.pool = nn.MaxPool1d(2, 2)\n",
368
        "        self.conv2 = nn.Conv1d(32, 64, kernel_size=3, stride=1, padding=1)\n",
369
        "        self.dropout = nn.Dropout(0.5)\n",
370
        "        self.fc1 = nn.Linear(64 * (seq_len // 4), 128)\n",
371
        "        self.fc2 = nn.Linear(128, num_classes)\n",
372
        "\n",
373
        "    def forward(self, x):\n",
374
        "        x = x.permute(0, 2, 1)\n",
375
        "        x = self.pool(nn.functional.relu(self.conv1(x)))\n",
376
        "        x = self.pool(nn.functional.relu(self.conv2(x)))\n",
377
        "        x = x.view(x.size(0), -1)\n",
378
        "        x = self.dropout(x)\n",
379
        "        x = nn.functional.relu(self.fc1(x))\n",
380
        "        x = self.fc2(x)\n",
381
        "        return x\n",
382
        "\n",
383
        "\n",
384
        "\n",
385
        "# Ejemplo de instanciación del modelo CNN\n",
386
        "num_classes = len(real_data['type'].unique())  # Número de tipos únicos de secuencias\n",
387
        "model = DNA_CNN(seq_length, num_classes)\n"
388
      ],
389
      "metadata": {
390
        "id": "8TDgFJESG2lm"
391
      },
392
      "execution_count": null,
393
      "outputs": []
394
    },
395
    {
396
      "cell_type": "markdown",
397
      "source": [
398
        "**Training loop functions**"
399
      ],
400
      "metadata": {
401
        "id": "9_xnbxUsGWHO"
402
      }
403
    },
404
    {
405
      "cell_type": "markdown",
406
      "source": [
407
        "**Entrenamiento y Validación**"
408
      ],
409
      "metadata": {
410
        "id": "AybSCEsMIrwt"
411
      }
412
    },
413
    {
414
      "cell_type": "code",
415
      "source": [
416
        "import torch.optim as optim\n",
417
        "# Definir el número de épocas\n",
418
        "num_epochs = 20  # Puedes ajustar este número según tus necesidades\n",
419
        "\n",
420
        "# Cambiar a un optimizador más eficiente como Adam\n",
421
        "optimizer = optim.Adam(model.parameters(), lr=0.001)\n",
422
        "\n",
423
        "# Definir la función de perdida\n",
424
        "loss_func = nn.CrossEntropyLoss()\n",
425
        "\n",
426
        "# Considera implementar una estrategia de ajuste de ratio de aprendizaje\n",
427
        "scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, 'min')\n",
428
        "\n",
429
        "def train_one_epoch(model, train_loader, loss_func, optimizer, device):\n",
430
        "    model.train()  # Establecer el modelo en modo de entrenamiento\n",
431
        "    total_loss = 0\n",
432
        "\n",
433
        "    for inputs, labels in train_loader:\n",
434
        "        inputs, labels = inputs.to(device), labels.to(device)\n",
435
        "\n",
436
        "        # Paso hacia adelante\n",
437
        "        outputs = model(inputs)\n",
438
        "        loss = loss_func(outputs, labels)\n",
439
        "\n",
440
        "        # Paso hacia atrás y optimización\n",
441
        "        optimizer.zero_grad()\n",
442
        "        loss.backward()\n",
443
        "        optimizer.step()\n",
444
        "\n",
445
        "        total_loss += loss.item()\n",
446
        "\n",
447
        "    avg_loss = total_loss / len(train_loader)\n",
448
        "    return avg_loss\n",
449
        "\n",
450
        "def validate(model, val_loader, loss_func, device):\n",
451
        "    model.eval()  # Establecer el modelo en modo de evaluación\n",
452
        "    total_loss = 0\n",
453
        "    with torch.no_grad():\n",
454
        "        for inputs, labels in val_loader:\n",
455
        "            inputs, labels = inputs.to(device), labels.to(device)\n",
456
        "            outputs = model(inputs)\n",
457
        "            loss = loss_func(outputs, labels)\n",
458
        "            total_loss += loss.item()\n",
459
        "\n",
460
        "    avg_loss = total_loss / len(val_loader)\n",
461
        "    return avg_loss\n",
462
        "\n",
463
        "# Ejemplo de cómo ejecutar el bucle de entrenamiento y validación\n",
464
        "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
465
        "model.to(device)\n",
466
        "\n",
467
        "train_losses = []\n",
468
        "val_losses = []\n",
469
        "\n",
470
        "for epoch in range(num_epochs):\n",
471
        "    train_loss = train_one_epoch(model, train_loader, loss_func, optimizer, device)\n",
472
        "    val_loss = validate(model, val_loader, loss_func, device)\n",
473
        "    scheduler.step(val_loss)\n",
474
        "    train_losses.append(train_loss)\n",
475
        "    val_losses.append(val_loss)\n",
476
        "    print(f'Epoch {epoch+1}/{num_epochs}, Train Loss: {train_loss:.4f}, Val Loss: {val_loss:.4f}')\n"
477
      ],
478
      "metadata": {
479
        "colab": {
480
          "base_uri": "https://localhost:8080/"
481
        },
482
        "id": "E96oNj04Ixw4",
483
        "outputId": "d23158bf-6925-4f79-e94d-7b2ba5b3ed47"
484
      },
485
      "execution_count": null,
486
      "outputs": [
487
        {
488
          "output_type": "stream",
489
          "name": "stdout",
490
          "text": [
491
            "Epoch 1/20, Train Loss: 0.9004, Val Loss: 0.5294\n",
492
            "Epoch 2/20, Train Loss: 0.4120, Val Loss: 0.2169\n",
493
            "Epoch 3/20, Train Loss: 0.2390, Val Loss: 0.1410\n",
494
            "Epoch 4/20, Train Loss: 0.1901, Val Loss: 0.1403\n",
495
            "Epoch 5/20, Train Loss: 0.1582, Val Loss: 0.1124\n",
496
            "Epoch 6/20, Train Loss: 0.1347, Val Loss: 0.1148\n",
497
            "Epoch 7/20, Train Loss: 0.1198, Val Loss: 0.1013\n",
498
            "Epoch 8/20, Train Loss: 0.0993, Val Loss: 0.1075\n",
499
            "Epoch 9/20, Train Loss: 0.1114, Val Loss: 0.1034\n",
500
            "Epoch 10/20, Train Loss: 0.1043, Val Loss: 0.1030\n",
501
            "Epoch 11/20, Train Loss: 0.0835, Val Loss: 0.1042\n",
502
            "Epoch 12/20, Train Loss: 0.0782, Val Loss: 0.1101\n",
503
            "Epoch 13/20, Train Loss: 0.0856, Val Loss: 0.1056\n",
504
            "Epoch 14/20, Train Loss: 0.0842, Val Loss: 0.0887\n",
505
            "Epoch 15/20, Train Loss: 0.0726, Val Loss: 0.1214\n",
506
            "Epoch 16/20, Train Loss: 0.0576, Val Loss: 0.1094\n",
507
            "Epoch 17/20, Train Loss: 0.0513, Val Loss: 0.0986\n",
508
            "Epoch 18/20, Train Loss: 0.0590, Val Loss: 0.1215\n",
509
            "Epoch 19/20, Train Loss: 0.0593, Val Loss: 0.0916\n",
510
            "Epoch 20/20, Train Loss: 0.0537, Val Loss: 0.1083\n"
511
          ]
512
        }
513
      ]
514
    },
515
    {
516
      "cell_type": "markdown",
517
      "source": [
518
        "**Evaluación del modelo**"
519
      ],
520
      "metadata": {
521
        "id": "8eZlM5EwI-3A"
522
      }
523
    },
524
    {
525
      "cell_type": "code",
526
      "source": [
527
        "from sklearn.metrics import classification_report, confusion_matrix\n",
528
        "import numpy as np\n",
529
        "\n",
530
        "def evaluate_model(model, test_loader, device):\n",
531
        "    model.eval()  # Establecer el modelo en modo de evaluación\n",
532
        "    all_preds = []\n",
533
        "    all_labels = []\n",
534
        "\n",
535
        "    with torch.no_grad():\n",
536
        "        for inputs, labels in test_loader:\n",
537
        "            inputs = inputs.to(device)\n",
538
        "            labels = labels.to(device)\n",
539
        "            outputs = model(inputs)\n",
540
        "\n",
541
        "            _, predicted = torch.max(outputs, 1)\n",
542
        "            all_preds.extend(predicted.cpu().numpy())\n",
543
        "            all_labels.extend(labels.cpu().numpy())\n",
544
        "\n",
545
        "    return all_preds, all_labels\n",
546
        "\n",
547
        "# Evaluar el modelo en el conjunto de prueba\n",
548
        "all_preds, all_labels = evaluate_model(model, test_loader, device)\n",
549
        "\n",
550
        "# Calcular y mostrar métricas de rendimiento\n",
551
        "print(classification_report(all_labels, all_preds))\n",
552
        "print(confusion_matrix(all_labels, all_preds))\n"
553
      ],
554
      "metadata": {
555
        "colab": {
556
          "base_uri": "https://localhost:8080/"
557
        },
558
        "id": "cJx0Qo67I1ho",
559
        "outputId": "60a845cb-0881-48d2-811b-3061fa9e1a71"
560
      },
561
      "execution_count": null,
562
      "outputs": [
563
        {
564
          "output_type": "stream",
565
          "name": "stdout",
566
          "text": [
567
            "              precision    recall  f1-score   support\n",
568
            "\n",
569
            "           0       0.98      0.94      0.96       122\n",
570
            "           1       0.89      0.99      0.94       117\n",
571
            "           2       1.00      0.97      0.98       240\n",
572
            "\n",
573
            "    accuracy                           0.97       479\n",
574
            "   macro avg       0.96      0.97      0.96       479\n",
575
            "weighted avg       0.97      0.97      0.97       479\n",
576
            "\n",
577
            "[[115   7   0]\n",
578
            " [  1 116   0]\n",
579
            " [  1   7 232]]\n"
580
          ]
581
        }
582
      ]
583
    },
584
    {
585
      "cell_type": "markdown",
586
      "source": [
587
        "**Gráficas**"
588
      ],
589
      "metadata": {
590
        "id": "aMQ9y8oIJrUS"
591
      }
592
    },
593
    {
594
      "cell_type": "code",
595
      "source": [
596
        "import matplotlib.pyplot as plt\n",
597
        "\n",
598
        "def plot_losses(train_losses, val_losses):\n",
599
        "    plt.plot(train_losses, label='Entrenamiento')\n",
600
        "    plt.plot(val_losses, label='Validación')\n",
601
        "    plt.title('Pérdida durante el Entrenamiento y Validación')\n",
602
        "    plt.xlabel('Épocas')\n",
603
        "    plt.ylabel('Pérdida')\n",
604
        "    plt.legend()\n",
605
        "    plt.show()\n",
606
        "\n",
607
        "plot_losses(train_losses, val_losses)\n"
608
      ],
609
      "metadata": {
610
        "colab": {
611
          "base_uri": "https://localhost:8080/",
612
          "height": 474
613
        },
614
        "id": "nHtBPYOWJqd_",
615
        "outputId": "b2c17fde-f097-4391-fce5-eb36efc0721b"
616
      },
617
      "execution_count": null,
618
      "outputs": [
619
        {
620
          "output_type": "display_data",
621
          "data": {
622
            "text/plain": [
623
              "<Figure size 640x480 with 1 Axes>"
624
            ],
625
            "image/png": "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smUYP348XnzxRURFReGrr77C8ePHsW/fvnKr4M+ePYvu3bsjKCgIM2bMgLOzM3755RcMGTIE69atw9ChQ6v0uqSmpiIxMdFomSRJht87U507UPp7UNnzmjZtGjw8PDBr1ixcu3YN8+bNw9SpU7F69WrDOsuWLYOLiwumT58OFxcX7Nq1C2+//TbS0tLw6aefGu3vzp07eOSRRzBixAg8+eSTWLhwIUaMGIGffvoJL7/8Mp5//nk89dRT+PTTT/HEE08gOjoarq6uZb6eK1aswNixYxEREYGPP/4YWVlZWLhwIR544AEcP37cKHhrtVpERESgS5cu+Oyzz7Bz5058/vnnCAsLw+TJk+Hj44OFCxdi8uTJGDp0KB577DEAQJs2bQAAO3fuRP/+/dGwYUPMnj0b2dnZmD9/Prp3745jx46V2cnWxcUFQ4cOxerVqzF37lyj2qeff/4ZQghDOClNz549ERwcjJUrV2L69OlGz61cuRJOTk4YMmQIAGDhwoVo2bIlBg8eDDs7O/z222944YUXoNPpMGXKlDKPUZo//vgDjz/+OFq0aIE5c+YgKSkJ48ePR3BwcIl1v/jiCwwePBijRo1CXl4eVq1ahSeffBKbN282+rv1zjvvYPbs2ejWrRveffddqFQqHDp0CLt27cLDDz9cajmys7PRu3dvXL58GVOnTkWDBg2wZs0ajBs3DikpKSVC470+OwRAkM1ZunSpACB27twpbt++LaKjo8WqVauEl5eXcHR0FDdv3hTXrl0TSqVSfPDBB0bbnj59WtjZ2Rkt79WrlwAgFi1aZLTuxo0bBQDxySefGJYVFBSIHj16CABi6dKlhuWzZs0SRX/dTpw4IQCIF154wWifTz31lAAgZs2aZViWlZVV4hwPHDggAIgffvih3NeiMmXs1auX6NWrV4l9jB07VtSvX9/wOCoqSgAQbm5uIiEhwWjdgoICkZuba7Tszp07ws/PTzzzzDMl9uHl5SWSk5MNy3/99VcBQPz222+GZVOmTBGlfVT/+usvAUD89NNPRsu3bdtW6vLiHnroIdG6dWuRk5NjWKbT6US3bt1E48aNDct2794tAIjdu3eXuz/9711pN7VabdJzL+89qOh56csbHh4udDqdYfl//vMfoVQqRUpKimFZab+DkyZNEk5OTkbH0X9WVq5caVh24cIFAUAoFApx8OBB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NVovLCwMOp0O586dKzF44F5lOXXqFHQ6nVHtjb7JU19Wqjj2uamjHnvsMSiVSrzzzjsl0r4QAklJSffcx4ABA1BQUGA0DFOr1Vboj5l+gqriM47OmzevxLpKpbJEGefPn19mjUpVyxgWFoYLFy4YDQM+efIk9u3bd8/jFC0rYPwf1KFDh3DgwIEK76M4/RdK8TA3bNgwaLVavPfeeyW2KSgoKDf8+fr6onfv3vjmm28QGxtb4vniQ6EtpaxzL4s5zqu09zQvLw9ff/11pfdVWREREXBzc8OHH35o1MSpV5Xz0Y8uLP6aBgQEoF27dli+fLnRc2fOnMEff/yBAQMGVGj/o0ePxh9//IF58+bBy8urUpPR1atXDz169MDq1avx448/okGDBujWrZvh+dLei9TU1BIhoyKKnm/RpsUdO3YYphQoelxJkoz+5ly7dg0bN240Wm/IkCFQKBR49913S9QilVerMmDAAMTFxRmN0CsoKMD8+fPh4uKCXr16Vfr86jrW3NRRYWFheP/99/H666/j2rVrGDJkCFxdXREVFYUNGzZg4sSJeOWVV8rdx6BBg9C9e3fMmDED165dQ4sWLbB+/foKdepr164dRo4cia+//hqpqano1q0bIiMjcfny5RLrPvLII1ixYgU0Gg1atGiBAwcOYOfOnSWGFle3jM888wzmzp2LiIgITJgwAQkJCVi0aBFatmxZop9FWR555BGsX78eQ4cOxcCBAxEVFYVFixahRYsWyMjIqNA+iuvQoQMAufN1RESEYabWXr16YdKkSZgzZw5OnDiBhx9+GPb29rh06RLWrFmDL774otwJwRYsWIAHHngArVu3xnPPPYeGDRsiPj4eBw4cwM2bN3Hy5Mkqlff3338v0ckWALp162aoWamoss69PKY+r27dusHDwwNjx47Fiy++CEmSsGLFihppAnBzc8PChQsxevRo3HfffRgxYgR8fHxw48YNbNmyBd27d8dXX31VqX06OjqiRYsWWL16NZo0aQJPT0+0atUKrVq1wqeffor+/fuja9eumDBhgmEouEajqfAs0U899RReffVVbNiwAZMnT6700OSnn34aEydORExMDN58802j5x5++GGoVCoMGjQIkyZNQkZGBhYvXgxfX99Sw+y9zJkzBwMHDsQDDzyAZ555BsnJyZg/fz5atmxp9HkdOHAg5s6d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\n"
626
          },
627
          "metadata": {}
628
        }
629
      ]
630
    },
631
    {
632
      "cell_type": "code",
633
      "source": [
634
        "import torch\n",
635
        "\n",
636
        "# Carga tu modelo (asegúrate de que esté en modo de evaluación)\n",
637
        "model.eval()\n",
638
        "\n",
639
        "# Cadena de ADN de ejemplo para clasificar\n",
640
        "dna_sequence = \"GCCCTGGCGCCCAGCACCATGAAGATCAAGGTGAGTCGAGGGGTTGGTGGCCCTCTGCCT\"  # Reemplaza esto con tu secuencia\n",
641
        "\n",
642
        "# Codificar la secuencia\n",
643
        "encoded_sequence = one_hot_encode(dna_sequence).unsqueeze(0)  # Agrega una dimensión de lote\n",
644
        "\n",
645
        "# Realizar la predicción\n",
646
        "with torch.no_grad():\n",
647
        "    output = model(encoded_sequence)\n",
648
        "    predicted_class = torch.argmax(output, dim=1)\n",
649
        "\n",
650
        "# Imprimir la clase predicha\n",
651
        "print(f\"Clase predicha: {predicted_class.item()}\")\n"
652
      ],
653
      "metadata": {
654
        "colab": {
655
          "base_uri": "https://localhost:8080/"
656
        },
657
        "id": "xqyq08iPSnI1",
658
        "outputId": "2b433fa3-6616-4ab9-e94f-1354155392f1"
659
      },
660
      "execution_count": null,
661
      "outputs": [
662
        {
663
          "output_type": "stream",
664
          "name": "stdout",
665
          "text": [
666
            "Clase predicha: 0\n"
667
          ]
668
        }
669
      ]
670
    }
671
  ]
672
}