Files
AIM-PIbd-31-Yaruskin-S-A/lab_12/laba12.ipynb
2025-05-24 04:33:24 +04:00

6.6 MiB
Raw Blame History

Лаб 12. Глубокие сверточные нейронные сети

Сначала поработаем с документами. Загрузим, почистит, классифицируем и воспользуемся глубоким обучением

In [1]:
import os
import pandas as pd
import spacy
from docx import Document
from docx.document import Document as DocumentType
from pandas import DataFrame
import numpy as np
import matplotlib.pyplot as plt


# Чтение текста из файла
def read_docx(file_path: str) -> str:
    # Считывает текст из файла .docx
    try:
        if not os.path.isfile(file_path):
            raise FileNotFoundError(f"Файл {file_path} не найден")

        document = Document(file_path)
        full_text: list[str] = [paragraph.text.strip() for paragraph in document.paragraphs if paragraph.text.strip()]
        return "\n".join(full_text)
    
    except Exception as e:
        print(f"Ошибка при чтении {file_path}: {e}")
        return ""

# Загрузка текста из файлов
def load_docx(dataset_path: str) -> DataFrame:
    # Загружает документы из уазанной директории в DataFrame
    if not os.path.isdir(dataset_path):
        raise NotADirectoryError(f"Директория {dataset_path} не найдена")

    documents = []
    for file_name in sorted(os.listdir(dataset_path)):
        if file_name.startswith("~$") or not file_name.lower().endswith(".docx"):
            continue
        file_path = os.path.join(dataset_path, file_name)
        text = read_docx(file_path)
        if text:
            doc_type = 0 if file_name.startswith("tz_") else 1
            documents.append((file_name, text, doc_type))
        
    df = pd.DataFrame(documents, columns=["doc", "text", "type"])
    df.sort_values(by="doc", inplace=True)
    return df
    

# Пути
project_root = os.path.abspath(os.path.join(os.getcwd(), ".."))
dataset_path: str = os.path.join(project_root, "static", "pmu")

# Загрузка данных
try:
    df = load_docx(dataset_path)
    df.info()

    # Вывод первых строк данных
    display(df.head())
except Exception as e:
    print(f"Ошибка загрузки данных: {e}")
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 58 entries, 0 to 57
Data columns (total 3 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   doc     58 non-null     object
 1   text    58 non-null     object
 2   type    58 non-null     int64 
dtypes: int64(1), object(2)
memory usage: 1.5+ KB
<style scoped=""> .dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; } </style>
doc text type
0 31-АнисинРС.docx МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИ... 1
1 31-АфанасьевСС.docx МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИ... 1
2 31-БакальскаяЕД.docx МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИ... 1
3 31-БарсуковПО.docx МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИ... 1
4 31-БелянинНН.docx МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИ... 1

Предобработка текстов

In [2]:
import nltk
import re
import unicodedata
from typing import Set

from num2words import num2words
from nltk.corpus import stopwords
from spacy.tokens.doc import Doc

# Загрузка модели Spacy. Модель для русского языка
spacy = spacy.load("ru_core_news_lg")

nltk.download("punkt")
nltk.download("stopwords")
stop_woards = set(stopwords.words("russian"))

# Список стоп-слов 
stop_woards: Set[str] = spacy.Defaults.stop_words

# Предобработка текста. Метод для предобработки текста
def preprocess_text(text):
    # Приведение текста к нижнему регистру
    text = text.lower()
    
    # Замена переносов на пробел
    text = text.replace("\n", " ")
    
    # Удаление всех символов, кроме букв и пробелов
    text = re.sub(r"[^a-zA-Za-яА-Я ]", "", text)

    # Удаление диакритических знаков
    text = unicodedata.normalize("NFKD", text)
    text = "".join([char for char in text if not unicodedata.combining(char)])

    # Преобразование чисел в слова
    words: list[str] = text.split()
    words = [num2words(word, lang="ru") if word.isdigit() else word for word in words]
    text = " ".join(words)

    # Токенизация с использованием Spacy
    document = spacy(text)

    # Удаление стоп-слов и пунктуации
    tokens = [token.text for token in document if token.text not in stop_woards and not token.is_stop 
              and not token.is_punct and not token.is_digit]
    
    return " ".join(tokens)


# Применение метода предобработки к загруженному тексту в DataFrame
df["preprocessed_text"] = df["text"].apply(preprocess_text)

# Вывод первых строк DataFrame с предобработанными текстами
display(df.head())
[nltk_data] Downloading package punkt to
[nltk_data]     C:\Users\salih\AppData\Roaming\nltk_data...
[nltk_data]   Package punkt is already up-to-date!
[nltk_data] Downloading package stopwords to
[nltk_data]     C:\Users\salih\AppData\Roaming\nltk_data...
[nltk_data]   Package stopwords is already up-to-date!
<style scoped=""> .dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; } </style>
doc text type preprocessed_text
0 31-АнисинРС.docx МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИ... 1 министерство науки высшего образования россиис...
1 31-АфанасьевСС.docx МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИ... 1 министерство науки высшего образования россиис...
2 31-БакальскаяЕД.docx МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИ... 1 министерство науки высшего образования россиис...
3 31-БарсуковПО.docx МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИ... 1 министерство науки высшего образования россиис...
4 31-БелянинНН.docx МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИ... 1 министерство науки высшего образования россиис...

Нормализация текста (Лемматизация)

In [3]:
from nltk.stem import PorterStemmer

# Функция Лемматизации текста
def lemmatization_text(text: str) -> str:
    doc = spacy(text)
    lemmas = [token.lemma_ for token in doc if not token.is_punct and not token.is_space]
    return " ".join(lemmas)


# Применение метода предобработки к каждому тексту в DataFrame
df["lemmatized_text"] = df["preprocessed_text"].apply(lemmatization_text)

# Вывод первых строк DataFrame с предобработанными текстами
display(df.head())
<style scoped=""> .dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; } </style>
doc text type preprocessed_text lemmatized_text
0 31-АнисинРС.docx МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИ... 1 министерство науки высшего образования россиис... министерство наука высокий образование россиис...
1 31-АфанасьевСС.docx МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИ... 1 министерство науки высшего образования россиис... министерство наука высокий образование россиис...
2 31-БакальскаяЕД.docx МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИ... 1 министерство науки высшего образования россиис... министерство наука высокий образование россиис...
3 31-БарсуковПО.docx МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИ... 1 министерство науки высшего образования россиис... министерство наука высокий образование россиис...
4 31-БелянинНН.docx МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИ... 1 министерство науки высшего образования россиис... министерство наука высокий образование россиис...

Фильтрация текста

In [4]:
# Функция фильтрации текста
def filter_text(text: str, min_length: int = 3, max_length: int = 15) -> str:
    words = text.split()

    # Удаление слов, где длина либо меньше минимального, либо больше
    return " ".join(word for word in words if min_length <= len(word) <= max_length)

# Применение фильтрации к лемматизированным текстам
df["filtered_text"] = df["lemmatized_text"].apply(filter_text)

# Вывод первых строк DataFrame с отфильтрованными текстами
display(df.head())
<style scoped=""> .dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; } </style>
doc text type preprocessed_text lemmatized_text filtered_text
0 31-АнисинРС.docx МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИ... 1 министерство науки высшего образования россиис... министерство наука высокий образование россиис... министерство наука высокий образование россиис...
1 31-АфанасьевСС.docx МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИ... 1 министерство науки высшего образования россиис... министерство наука высокий образование россиис... министерство наука высокий образование россиис...
2 31-БакальскаяЕД.docx МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИ... 1 министерство науки высшего образования россиис... министерство наука высокий образование россиис... министерство наука высокий образование россиис...
3 31-БарсуковПО.docx МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИ... 1 министерство науки высшего образования россиис... министерство наука высокий образование россиис... министерство наука высокий образование россиис...
4 31-БелянинНН.docx МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИ... 1 министерство науки высшего образования россиис... министерство наука высокий образование россиис... министерство наука высокий образование россиис...

Векторизация

In [5]:
from scipy.sparse._matrix import spmatrix
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.cluster import KMeans

# Создание объекта TfidVectorizer
tfidf_vectorizer = TfidfVectorizer(max_df=0.85, sublinear_tf=True)

# Применяем векторизацию к отфильтрованным фильтрам
tfidf_matrix: spmatrix = tfidf_vectorizer.fit_transform(df["filtered_text"])

# Преобразуем результат в DataFrame для удобства
tfidf_df = pd.DataFrame(tfidf_matrix.toarray(), columns=tfidf_vectorizer.get_feature_names_out())

# Вывод первые строки результата
display(tfidf_df.head())

# Преобразуем тексты в списки индексов
X = tfidf_df
y = np.random.randint(0, 2, size=X.shape[0])

# Кластеризация KMeans
num_clusters = 3
kmeans = KMeans(n_clusters=num_clusters, random_state=42)
clusters = kmeans.fit_predict(tfidf_matrix)

print("Кластеры созданы:", clusters)
<style scoped=""> .dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; } </style>
abbreviated abilities aboba about abrath acceleration accent accept access accidentally ... яркии яркий яркооранжевым ярлык ярусин ярускин ярускина ясно ясный яшин
0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.000000 0.000000 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
1 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.023694 0.069332 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
2 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.000000 0.000000 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
3 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.000000 0.000000 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
4 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.020470 0.000000 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0

5 rows × 9466 columns

Кластеры созданы: [2 2 0 2 2 0 2 2 2 0 2 2 0 1 2 0 2 2 0 0 0 0 2 1 2 0 0 0 2 0 0 0 0 1 1 0 1
 1 1 1 1 1 0 1 1 1 2 2 2 1 2 2 0 0 2 2 1 1]

Разделение на выборки

In [6]:
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, f1_score

# Разделение на выборки
train_indices, test_indices = train_test_split(df.index, test_size=0.2, random_state=42)

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Устанавливаем количество эпох
epochs = 10

Теперь осталось спроектировать архитектуру глубоких нейронных сетей для классификации текстов

Инициализация Keras

In [7]:
import jax
print(jax.__version__)

import keras
print(keras.__version__)
0.6.1
3.10.0
In [11]:
import os
os.environ["KERAS_BACKEND"] = "jax"

import keras
print("Keras version:", keras.__version__)

from keras.models import Sequential
from keras.layers import (
    InputLayer,
    Embedding,
    SpatialDropout1D,
    LSTM,
    Dense,
    Conv1D,
    MaxPooling1D,
    Flatten,
    GlobalMaxPooling1D,
    BatchNormalization,
    Dropout
)
Keras version: 3.10.0

Теперь обучаю модель классификации

In [12]:
model = Sequential()
model.add(Embedding(5000, 64, input_length=X_train.shape[1]))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(BatchNormalization())
model.add(Dropout(0.5))
model.add(Dense(64, activation='relu'))
model.add(Dense(1, activation='sigmoid'))  # Для бинарной классификации
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
d:\PythonPr\AIM-PIbd-31-Yaruskin-S-A\.venv\Lib\site-packages\keras\src\layers\core\embedding.py:97: UserWarning: Argument `input_length` is deprecated. Just remove it.
  warnings.warn(
In [13]:
model.summary()

model.build(input_shape=(None, X_train))
    
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])

mlp_history = model.fit(X_train, y_train, epochs=epochs, batch_size=32, validation_split=0.2, verbose=1)
Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                     Output Shape                  Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ embedding (Embedding)           │ ?                      │   0 (unbuilt) │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ flatten (Flatten)               │ ?                      │   0 (unbuilt) │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense (Dense)                   │ ?                      │   0 (unbuilt) │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ batch_normalization             │ ?                      │   0 (unbuilt) │
│ (BatchNormalization)            │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dropout (Dropout)               │ ?                      │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_1 (Dense)                 │ ?                      │   0 (unbuilt) │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_2 (Dense)                 │ ?                      │   0 (unbuilt) │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 0 (0.00 B)
 Trainable params: 0 (0.00 B)
 Non-trainable params: 0 (0.00 B)
Epoch 1/10
2/2 ━━━━━━━━━━━━━━━━━━━━ 7s 1s/step - accuracy: 0.5972 - loss: 0.6934 - val_accuracy: 0.5000 - val_loss: 0.6933
Epoch 2/10
2/2 ━━━━━━━━━━━━━━━━━━━━ 2s 1s/step - accuracy: 0.5764 - loss: 0.6925 - val_accuracy: 0.5000 - val_loss: 0.6932
Epoch 3/10
2/2 ━━━━━━━━━━━━━━━━━━━━ 3s 1s/step - accuracy: 0.5972 - loss: 0.6926 - val_accuracy: 0.5000 - val_loss: 0.6932
Epoch 4/10
2/2 ━━━━━━━━━━━━━━━━━━━━ 2s 1s/step - accuracy: 0.5764 - loss: 0.6917 - val_accuracy: 0.5000 - val_loss: 0.6932
Epoch 5/10
2/2 ━━━━━━━━━━━━━━━━━━━━ 3s 1s/step - accuracy: 0.5868 - loss: 0.6913 - val_accuracy: 0.5000 - val_loss: 0.6932
Epoch 6/10
2/2 ━━━━━━━━━━━━━━━━━━━━ 3s 1s/step - accuracy: 0.5764 - loss: 0.6900 - val_accuracy: 0.5000 - val_loss: 0.6932
Epoch 7/10
2/2 ━━━━━━━━━━━━━━━━━━━━ 2s 1s/step - accuracy: 0.5868 - loss: 0.6908 - val_accuracy: 0.5000 - val_loss: 0.6932
Epoch 8/10
2/2 ━━━━━━━━━━━━━━━━━━━━ 2s 1s/step - accuracy: 0.5868 - loss: 0.6892 - val_accuracy: 0.5000 - val_loss: 0.6931
Epoch 9/10
2/2 ━━━━━━━━━━━━━━━━━━━━ 2s 1s/step - accuracy: 0.5972 - loss: 0.6868 - val_accuracy: 0.5000 - val_loss: 0.6931
Epoch 10/10
2/2 ━━━━━━━━━━━━━━━━━━━━ 3s 1s/step - accuracy: 0.5972 - loss: 0.6856 - val_accuracy: 0.5000 - val_loss: 0.6931

Создание сверточной модели глубокого обучения

In [14]:
model_cnn = Sequential()
model_cnn.add(Embedding(input_dim=5000, output_dim=128, input_length=X_train.shape[1]))
model_cnn.add(Conv1D(64, kernel_size=5, activation='relu'))
model_cnn.add(MaxPooling1D(pool_size=2))
model_cnn.add(Conv1D(128, kernel_size=5, activation='relu'))
model_cnn.add(MaxPooling1D(pool_size=5))
model_cnn.add(GlobalMaxPooling1D())
model_cnn.add(Dense(64, activation='relu'))
model_cnn.add(BatchNormalization())
model_cnn.add(Dropout(0.5))
model_cnn.add(Dense(1, activation='sigmoid')) 

model_cnn.summary()
d:\PythonPr\AIM-PIbd-31-Yaruskin-S-A\.venv\Lib\site-packages\keras\src\layers\core\embedding.py:97: UserWarning: Argument `input_length` is deprecated. Just remove it.
  warnings.warn(
Model: "sequential_1"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                     Output Shape                  Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ embedding_1 (Embedding)         │ ?                      │   0 (unbuilt) │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv1d (Conv1D)                 │ ?                      │   0 (unbuilt) │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ max_pooling1d (MaxPooling1D)    │ ?                      │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv1d_1 (Conv1D)               │ ?                      │   0 (unbuilt) │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ max_pooling1d_1 (MaxPooling1D)  │ ?                      │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ global_max_pooling1d            │ ?                      │             0 │
│ (GlobalMaxPooling1D)            │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_3 (Dense)                 │ ?                      │   0 (unbuilt) │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ batch_normalization_1           │ ?                      │   0 (unbuilt) │
│ (BatchNormalization)            │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dropout_1 (Dropout)             │ ?                      │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_4 (Dense)                 │ ?                      │   0 (unbuilt) │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 0 (0.00 B)
 Trainable params: 0 (0.00 B)
 Non-trainable params: 0 (0.00 B)
In [15]:
model_cnn.compile(
    loss="binary_crossentropy",
    optimizer="adam",
    metrics=["accuracy"],
)

cnn_history = model_cnn.fit(
    X_train,
    y_train,
    batch_size=128,
    epochs=10,
    validation_data=(X_test, y_test)
)
Epoch 1/10
1/1 ━━━━━━━━━━━━━━━━━━━━ 9s 9s/step - accuracy: 0.5652 - loss: 0.6931 - val_accuracy: 0.5000 - val_loss: 0.6932
Epoch 2/10
1/1 ━━━━━━━━━━━━━━━━━━━━ 6s 6s/step - accuracy: 0.5652 - loss: 0.6928 - val_accuracy: 0.5000 - val_loss: 0.6932
Epoch 3/10
1/1 ━━━━━━━━━━━━━━━━━━━━ 6s 6s/step - accuracy: 0.5652 - loss: 0.6922 - val_accuracy: 0.5000 - val_loss: 0.6933
Epoch 4/10
1/1 ━━━━━━━━━━━━━━━━━━━━ 5s 5s/step - accuracy: 0.5652 - loss: 0.6920 - val_accuracy: 0.5000 - val_loss: 0.6934
Epoch 5/10
1/1 ━━━━━━━━━━━━━━━━━━━━ 5s 5s/step - accuracy: 0.5652 - loss: 0.6915 - val_accuracy: 0.5000 - val_loss: 0.6935
Epoch 6/10
1/1 ━━━━━━━━━━━━━━━━━━━━ 5s 5s/step - accuracy: 0.5652 - loss: 0.6907 - val_accuracy: 0.5000 - val_loss: 0.6936
Epoch 7/10
1/1 ━━━━━━━━━━━━━━━━━━━━ 5s 5s/step - accuracy: 0.5652 - loss: 0.6906 - val_accuracy: 0.5000 - val_loss: 0.6937
Epoch 8/10
1/1 ━━━━━━━━━━━━━━━━━━━━ 5s 5s/step - accuracy: 0.5652 - loss: 0.6891 - val_accuracy: 0.5000 - val_loss: 0.6938
Epoch 9/10
1/1 ━━━━━━━━━━━━━━━━━━━━ 6s 6s/step - accuracy: 0.5652 - loss: 0.6889 - val_accuracy: 0.5000 - val_loss: 0.6940
Epoch 10/10
1/1 ━━━━━━━━━━━━━━━━━━━━ 5s 5s/step - accuracy: 0.5652 - loss: 0.6900 - val_accuracy: 0.5000 - val_loss: 0.6941

Архитектуры глубокой рекуррентной нейронной сети LSTM

In [16]:
lstm_model = Sequential()
lstm_model.add(InputLayer(shape=(100,), dtype="float32"))
lstm_model.add(Embedding(1000, 64))
lstm_model.add(SpatialDropout1D(0.2))
lstm_model.add(LSTM(256, dropout=0.2))
lstm_model.add(Dense(1, activation="sigmoid"))

lstm_model.summary()
Model: "sequential_2"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                     Output Shape                  Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ embedding_2 (Embedding)         │ (None, 100, 64)        │        64,000 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ spatial_dropout1d               │ (None, 100, 64)        │             0 │
│ (SpatialDropout1D)              │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ lstm (LSTM)                     │ (None, 256)            │       328,704 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_5 (Dense)                 │ (None, 1)              │           257 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 392,961 (1.50 MB)
 Trainable params: 392,961 (1.50 MB)
 Non-trainable params: 0 (0.00 B)
In [17]:
lstm_model.compile(
    loss="binary_crossentropy",
    optimizer="adam",
    metrics=["accuracy"],
)

lstm_history = lstm_model.fit(
    X_train,
    y_train,
    batch_size=128,
    epochs=4,
    validation_data=(X_test, y_test)
)
Epoch 1/4
1/1 ━━━━━━━━━━━━━━━━━━━━ 105s 105s/step - accuracy: 0.4565 - loss: 0.6935 - val_accuracy: 0.5000 - val_loss: 0.6947
Epoch 2/4
1/1 ━━━━━━━━━━━━━━━━━━━━ 106s 106s/step - accuracy: 0.5652 - loss: 0.6878 - val_accuracy: 0.5000 - val_loss: 0.6989
Epoch 3/4
1/1 ━━━━━━━━━━━━━━━━━━━━ 124s 124s/step - accuracy: 0.5652 - loss: 0.6847 - val_accuracy: 0.5000 - val_loss: 0.7062
Epoch 4/4
1/1 ━━━━━━━━━━━━━━━━━━━━ 117s 117s/step - accuracy: 0.5652 - loss: 0.6804 - val_accuracy: 0.5000 - val_loss: 0.7136
In [18]:
lstm_model.evaluate(X_test, y_test)
1/1 ━━━━━━━━━━━━━━━━━━━━ 2s 2s/step - accuracy: 0.5000 - loss: 0.7136
Out[18]:
[0.7136409282684326, 0.5]

Визуализация распределения вероятностей результатов модели на валидационной выборке

In [19]:
plt.hist(lstm_model.predict(X_test))
_ = plt.axvline(x=0.5, color="orange")
1/1 ━━━━━━━━━━━━━━━━━━━━ 4s 4s/step
No description has been provided for this image

теперь сравним модели

In [20]:
# Сравнение моделей
def evaluate_model(model, X_test, y_test):
    # Получаем предсказания
    y_pred = model.predict(X_test)
    
    # Преобразуем предсказания в бинарный формат
    if y_pred.ndim > 1 and y_pred.shape[1] > 1:
        y_pred = np.argmax(y_pred, axis=1)
    else:
        y_pred = (y_pred > 0.5).astype(int).flatten()
    
    # Преобразуем y_test если нужно
    if y_test.ndim > 1 and y_test.shape[1] > 1:
        y_test = np.argmax(y_test, axis=1)
    
    # Вычисляем метрики
    accuracy = accuracy_score(y_test, y_pred)
    f1 = f1_score(y_test, y_pred)
    
    return accuracy, f1

mlp_accuracy, mlp_f1 = evaluate_model(model, X_test, y_test)
cnn_accuracy, cnn_f1 = evaluate_model(model_cnn, X_test, y_test)
rnn_accuracy, rnn_f1 = evaluate_model(lstm_model, X_test, y_test)

# Вывод результатов
print(f'MLP Accuracy: {mlp_accuracy}, F1 Score: {mlp_f1}')
print(f'CNN Accuracy: {cnn_accuracy}, F1 Score: {cnn_f1}')
print(f'RNN Accuracy: {rnn_accuracy}, F1 Score: {rnn_f1}')
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 282ms/step
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 233ms/step
1/1 ━━━━━━━━━━━━━━━━━━━━ 2s 2s/step
MLP Accuracy: 0.5, F1 Score: 0.0
CNN Accuracy: 0.5, F1 Score: 0.0
RNN Accuracy: 0.5, F1 Score: 0.0
In [21]:
# Построение диаграмм производительности моделей
labels = ['Полносвязная', 'Свёрточная', 'Рекурентная']
accuracy_scores = [mlp_accuracy, cnn_accuracy, rnn_accuracy]
f1_scores = [mlp_f1, cnn_f1, rnn_f1]

x = np.arange(len(labels))  # метки по оси X
width = 0.15  # ширина столбиков

fig, ax = plt.subplots()
bars1 = ax.bar(x - width/2, accuracy_scores, width, label='Accuracy')
bars2 = ax.bar(x + width/2, f1_scores, width, label='F1 Score')

# Добавление аннотаций
ax.set_ylabel('Scores')
ax.set_title('Model Performance Comparison')
ax.set_xticks(x)
ax.set_xticklabels(labels)
ax.legend()

# Отображаем значения на столбиках
def add_value_labels(bars):
    for bar in bars:
        height = bar.get_height()
        ax.annotate(f'{height:.2f}',
                    xy=(bar.get_x() + bar.get_width() / 2, height),
                    xytext=(0, 3),  # 3 points vertical offset
                    textcoords="offset points",
                    ha='center', va='bottom')

add_value_labels(bars1)
add_value_labels(bars2)

plt.show()  # Отображаем график
No description has been provided for this image

Все 3 модели показади одинаково высокие результаты и показатели метрик точности и F1-оценки.

Выведим результат класторизации документов

In [22]:
from keras.models import Model, Sequential
from keras.layers import Input, Dense
from sklearn.cluster import KMeans
from sklearn.manifold import TSNE

# Параметры
input_dim = X_train.shape[1]  # Размерность входных данных (TF-IDF)
latent_dim = 64  # Размерность скрытого представления

# Encoder
encoder = Sequential([
    Dense(512, activation='relu', input_shape=(input_dim,)),
    Dense(latent_dim, activation='relu')
])

# Decoder
decoder = Sequential([
    Dense(512, activation='relu', input_shape=(latent_dim,)),
    Dense(input_dim, activation='sigmoid')
])

# Autoencoder
autoencoder = Sequential([encoder, decoder])
autoencoder.compile(optimizer='adam', loss='mse')

# Обучение
autoencoder.fit(X_train, X_train, 
                epochs=50, 
                batch_size=32, 
                validation_data=(X_test, X_test))

# Извлечение скрытых представлений
latent_representations = encoder.predict(X_train)

# Кластеризация
kmeans = KMeans(n_clusters=4, random_state=42)
clusters = kmeans.fit_predict(latent_representations)

tsne = TSNE(n_components=2, random_state=42)
X_2d = tsne.fit_transform(X_train)
Epoch 1/50
d:\PythonPr\AIM-PIbd-31-Yaruskin-S-A\.venv\Lib\site-packages\keras\src\layers\core\dense.py:93: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.
  super().__init__(activity_regularizer=activity_regularizer, **kwargs)
2/2 ━━━━━━━━━━━━━━━━━━━━ 2s 451ms/step - loss: 0.2474 - val_loss: 0.2469
Epoch 2/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 1s 308ms/step - loss: 0.2469 - val_loss: 0.2463
Epoch 3/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 256ms/step - loss: 0.2463 - val_loss: 0.2456
Epoch 4/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 262ms/step - loss: 0.2454 - val_loss: 0.2440
Epoch 5/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 311ms/step - loss: 0.2437 - val_loss: 0.2402
Epoch 6/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 235ms/step - loss: 0.2390 - val_loss: 0.2303
Epoch 7/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 225ms/step - loss: 0.2270 - val_loss: 0.2077
Epoch 8/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 266ms/step - loss: 0.1996 - val_loss: 0.1638
Epoch 9/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 233ms/step - loss: 0.1486 - val_loss: 0.0969
Epoch 10/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 1s 366ms/step - loss: 0.0768 - val_loss: 0.0326
Epoch 11/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 253ms/step - loss: 0.0194 - val_loss: 0.0053
Epoch 12/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 1s 340ms/step - loss: 0.0018 - val_loss: 6.6911e-04
Epoch 13/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 1s 289ms/step - loss: 1.7260e-04 - val_loss: 1.5852e-04
Epoch 14/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 250ms/step - loss: 1.0230e-04 - val_loss: 1.0857e-04
Epoch 15/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 280ms/step - loss: 1.0476e-04 - val_loss: 1.0494e-04
Epoch 16/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 241ms/step - loss: 1.0546e-04 - val_loss: 1.0517e-04
Epoch 17/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 259ms/step - loss: 1.0561e-04 - val_loss: 1.0542e-04
Epoch 18/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 273ms/step - loss: 1.0563e-04 - val_loss: 1.0554e-04
Epoch 19/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 1s 246ms/step - loss: 1.0564e-04 - val_loss: 1.0559e-04
Epoch 20/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 244ms/step - loss: 1.0564e-04 - val_loss: 1.0561e-04
Epoch 21/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 230ms/step - loss: 1.0564e-04 - val_loss: 1.0563e-04
Epoch 22/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 256ms/step - loss: 1.0564e-04 - val_loss: 1.0563e-04
Epoch 23/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 234ms/step - loss: 1.0564e-04 - val_loss: 1.0563e-04
Epoch 24/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 223ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 25/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 1s 289ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 26/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 230ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 27/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 219ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 28/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 225ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 29/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 232ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 30/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 240ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 31/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 271ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 32/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 234ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 33/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 214ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 34/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 222ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 35/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 232ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 36/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 215ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 37/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 229ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 38/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 235ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 39/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 214ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 40/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 216ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 41/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 240ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 42/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 223ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 43/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 216ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 44/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 229ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 45/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 227ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 46/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 221ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 47/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 224ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 48/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 217ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 49/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 213ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
Epoch 50/50
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 224ms/step - loss: 1.0564e-04 - val_loss: 1.0564e-04
1/2 ━━━━━━━━━━━━━━━━━━━━ 0s 42ms/stepWARNING:tensorflow:5 out of the last 6 calls to <function TensorFlowTrainer.make_predict_function.<locals>.one_step_on_data_distributed at 0x0000021BD07AE980> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has reduce_retracing=True option that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/guide/function#controlling_retracing and https://www.tensorflow.org/api_docs/python/tf/function for  more details.
2/2 ━━━━━━━━━━━━━━━━━━━━ 0s 43ms/step
In [23]:
# Визуализация
plt.figure(figsize=(15, 8))
for cluster_id in range(4): 
    indices = kmeans.labels_ == cluster_id
    plt.scatter(X_2d[indices, 0], X_2d[indices, 1], label=f"Кластер {cluster_id + 1}", alpha=0.7)

    for i, doc_name in enumerate(df.loc[train_indices, "doc"].values[indices]):
        x, y = X_2d[indices][i, 0], X_2d[indices][i, 1]
        plt.annotate(doc_name, (x, y), fontsize=8, alpha=0.8)

plt.title("Кластеры курсовых записок")
plt.legend()
plt.tight_layout()
plt.show()
No description has been provided for this image

Тепрь к анализу изображений. Будет использовать те же, что и в лаба 9 и 11

In [28]:
import os
import cv2
import numpy as np
import matplotlib.pyplot as plt

# Функция для загрузки изображений из папки на OpenCV
# Функция загрузки изображений
def load_images_from_folder(folder, target_size=(512, 512)):
    images = []
    labels = []
    for label in os.listdir(folder):
        if label in ['cat', 'lion']:
            label_folder = os.path.join(folder, label)
            if os.path.isdir(label_folder):
                for filename in os.listdir(label_folder):
                    img_path = os.path.join(label_folder, filename)
                    img = cv2.imread(img_path)
                    if img is not None:
                        img_resized = cv2.resize(img, target_size)
                        images.append(img_resized)
                        labels.append(label)
    return images, labels
# Путь к папке с изображениями
folder_path = "../static/photosForLab11/test"
images, labels = load_images_from_folder(folder_path)

images = np.array(images)
labels = np.array(labels)

# Разделение данных на обучающую и тестовую выборки
X_train, X_test, y_train, y_test = train_test_split(images, labels, test_size=0.2, random_state=42)

Сейчас преобработаем изображения

Изменим размер, преобразуем в оттенок серого и гистограмму

In [29]:
# Функция предобработки изображений
def preprocess_images(images):
    processed_images = []
    for img in images:

        img_resized = cv2.resize(img, (128, 128))
        # Преобразование в оттенки серого
        img_gray = cv2.cvtColor(img_resized, cv2.COLOR_BGR2GRAY)
        img_eq = cv2.equalizeHist(img_gray)
        processed_images.append(img_eq.reshape(128, 128, 1))

    return np.array(processed_images)

processed_images = preprocess_images(X_train)

Аугментируем наши данные, чтобы увеличить размер входных данных

In [30]:
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras import layers, models

# Функция аугментации данных
def augment_data(images, labels):
    datagen = ImageDataGenerator(
        rotation_range=15,
        width_shift_range=0.2,
        height_shift_range=0.2,
        shear_range=0.2,
        zoom_range=0.2,
        horizontal_flip=True,
        fill_mode='nearest'
    )

    augmented_images = []
    augmented_labels = []

    for label in np.unique(labels):
        label_images = images[labels == label]
        for img in label_images:
            img = img.reshape((1,) + img.shape)
            # Генерация 50 аугментированных изображений
            for _ in range(10):
                aug_iter = datagen.flow(img)
                aug_img = next(aug_iter)[0].astype(np.uint8)
                augmented_images.append(aug_img)
                augmented_labels.append(label)

    return np.array(augmented_images), np.array(augmented_labels)

augmented_images, augmented_labels = augment_data(processed_images, y_train)

кодировка меток

In [33]:
from sklearn.preprocessing import LabelEncoder

# Кодировка меток
label_encoder = LabelEncoder()
y_train_encoded = label_encoder.fit_transform(augmented_labels)

# Предобработка тестовых данных
X_test_processed = preprocess_images(X_test)

input_shape = (128, 128, 1)  # Одноканальное изображение
num_classes = len(np.unique(labels))

os.environ["KERAS_BACKEND"] = "torch"

Создаем модель AlexNet

In [34]:
model_AlexNet = models.Sequential()
model_AlexNet.add(layers.Conv2D(96, kernel_size=(11, 11), strides=(4, 4), activation='relu', input_shape=input_shape))
model_AlexNet.add(layers.MaxPooling2D(pool_size=(3, 3), strides=(2, 2)))
model_AlexNet.add(layers.Conv2D(256, kernel_size=(5, 5), padding='same', activation='relu'))
model_AlexNet.add(layers.MaxPooling2D(pool_size=(3, 3), strides=(2, 2)))
model_AlexNet.add(layers.Conv2D(384, kernel_size=(3, 3), padding='same', activation='relu'))
model_AlexNet.add(layers.Conv2D(384, kernel_size=(3, 3), padding='same', activation='relu'))
model_AlexNet.add(layers.Conv2D(256, kernel_size=(3, 3), padding='same', activation='relu'))
model_AlexNet.add(layers.MaxPooling2D(pool_size=(3, 3), strides=(2, 2)))
model_AlexNet.add(layers.Flatten())
model_AlexNet.add(layers.Dense(4096, activation='relu'))
model_AlexNet.add(layers.Dropout(0.5))
model_AlexNet.add(layers.Dense(4096, activation='relu'))
model_AlexNet.add(layers.Dropout(0.5))
model_AlexNet.add(layers.Dense(num_classes, activation='softmax'))

model_AlexNet.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])

model_AlexNet.summary()
d:\PythonPr\AIM-PIbd-31-Yaruskin-S-A\.venv\Lib\site-packages\keras\src\layers\convolutional\base_conv.py:113: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.
  super().__init__(activity_regularizer=activity_regularizer, **kwargs)
Model: "sequential_6"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                     Output Shape                  Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ conv2d (Conv2D)                 │ (None, 30, 30, 96)     │        11,712 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ max_pooling2d (MaxPooling2D)    │ (None, 14, 14, 96)     │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_1 (Conv2D)               │ (None, 14, 14, 256)    │       614,656 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ max_pooling2d_1 (MaxPooling2D)  │ (None, 6, 6, 256)      │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_2 (Conv2D)               │ (None, 6, 6, 384)      │       885,120 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_3 (Conv2D)               │ (None, 6, 6, 384)      │     1,327,488 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_4 (Conv2D)               │ (None, 6, 6, 256)      │       884,992 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ max_pooling2d_2 (MaxPooling2D)  │ (None, 2, 2, 256)      │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ flatten_1 (Flatten)             │ (None, 1024)           │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_10 (Dense)                │ (None, 4096)           │     4,198,400 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dropout_2 (Dropout)             │ (None, 4096)           │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_11 (Dense)                │ (None, 4096)           │    16,781,312 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dropout_3 (Dropout)             │ (None, 4096)           │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_12 (Dense)                │ (None, 2)              │         8,194 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 24,711,874 (94.27 MB)
 Trainable params: 24,711,874 (94.27 MB)
 Non-trainable params: 0 (0.00 B)

обучаем модель

In [35]:
# Обучение модели
history = model_AlexNet.fit(augmented_images, y_train_encoded, epochs=20, batch_size=32, validation_data=(X_test_processed, label_encoder.transform(y_test)))
Epoch 1/20
20/20 ━━━━━━━━━━━━━━━━━━━━ 14s 560ms/step - accuracy: 0.5087 - loss: 23.5193 - val_accuracy: 0.3125 - val_loss: 0.7374
Epoch 2/20
20/20 ━━━━━━━━━━━━━━━━━━━━ 12s 603ms/step - accuracy: 0.5518 - loss: 0.6863 - val_accuracy: 0.3125 - val_loss: 0.7402
Epoch 3/20
20/20 ━━━━━━━━━━━━━━━━━━━━ 13s 667ms/step - accuracy: 0.5554 - loss: 0.6802 - val_accuracy: 0.3125 - val_loss: 0.6985
Epoch 4/20
20/20 ━━━━━━━━━━━━━━━━━━━━ 12s 610ms/step - accuracy: 0.5365 - loss: 0.6881 - val_accuracy: 0.3125 - val_loss: 0.7326
Epoch 5/20
20/20 ━━━━━━━━━━━━━━━━━━━━ 11s 551ms/step - accuracy: 0.5800 - loss: 0.6865 - val_accuracy: 0.3125 - val_loss: 0.7708
Epoch 6/20
20/20 ━━━━━━━━━━━━━━━━━━━━ 11s 539ms/step - accuracy: 0.5302 - loss: 0.6960 - val_accuracy: 0.3125 - val_loss: 0.6964
Epoch 7/20
20/20 ━━━━━━━━━━━━━━━━━━━━ 12s 588ms/step - accuracy: 0.5573 - loss: 0.6900 - val_accuracy: 0.3125 - val_loss: 1.0781
Epoch 8/20
20/20 ━━━━━━━━━━━━━━━━━━━━ 11s 575ms/step - accuracy: 0.5321 - loss: 0.6927 - val_accuracy: 0.3125 - val_loss: 0.6791
Epoch 9/20
20/20 ━━━━━━━━━━━━━━━━━━━━ 11s 544ms/step - accuracy: 0.5386 - loss: 0.6820 - val_accuracy: 0.3125 - val_loss: 0.6984
Epoch 10/20
20/20 ━━━━━━━━━━━━━━━━━━━━ 21s 551ms/step - accuracy: 0.5416 - loss: 0.6890 - val_accuracy: 0.3125 - val_loss: 0.9109
Epoch 11/20
20/20 ━━━━━━━━━━━━━━━━━━━━ 11s 533ms/step - accuracy: 0.5308 - loss: 0.6733 - val_accuracy: 0.3125 - val_loss: 0.7041
Epoch 12/20
20/20 ━━━━━━━━━━━━━━━━━━━━ 11s 533ms/step - accuracy: 0.5500 - loss: 0.6682 - val_accuracy: 0.3125 - val_loss: 0.9367
Epoch 13/20
20/20 ━━━━━━━━━━━━━━━━━━━━ 11s 533ms/step - accuracy: 0.5952 - loss: 0.6489 - val_accuracy: 0.3125 - val_loss: 1.7096
Epoch 14/20
20/20 ━━━━━━━━━━━━━━━━━━━━ 11s 538ms/step - accuracy: 0.5538 - loss: 0.8030 - val_accuracy: 0.5000 - val_loss: 0.6839
Epoch 15/20
20/20 ━━━━━━━━━━━━━━━━━━━━ 11s 568ms/step - accuracy: 0.5845 - loss: 0.6835 - val_accuracy: 0.3750 - val_loss: 0.7041
Epoch 16/20
20/20 ━━━━━━━━━━━━━━━━━━━━ 11s 543ms/step - accuracy: 0.6425 - loss: 0.6561 - val_accuracy: 0.5625 - val_loss: 1.0126
Epoch 17/20
20/20 ━━━━━━━━━━━━━━━━━━━━ 11s 563ms/step - accuracy: 0.6515 - loss: 0.6445 - val_accuracy: 0.3750 - val_loss: 0.8138
Epoch 18/20
20/20 ━━━━━━━━━━━━━━━━━━━━ 12s 576ms/step - accuracy: 0.7035 - loss: 0.5704 - val_accuracy: 0.6875 - val_loss: 0.6534
Epoch 19/20
20/20 ━━━━━━━━━━━━━━━━━━━━ 11s 567ms/step - accuracy: 0.7503 - loss: 0.5291 - val_accuracy: 0.5625 - val_loss: 0.7053
Epoch 20/20
20/20 ━━━━━━━━━━━━━━━━━━━━ 11s 538ms/step - accuracy: 0.7670 - loss: 0.4688 - val_accuracy: 0.6875 - val_loss: 1.0335

оценка результат

In [36]:
# Визуализация процесса обучения
plt.plot(history.history['accuracy'], label='Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
plt.show()
No description has been provided for this image
In [37]:
import numpy as np
from sklearn.metrics import classification_report, confusion_matrix
import seaborn as sns

# 1. Предобработка меток теста
y_test_encoded = label_encoder.transform(y_test)

# 2. Оценка на всем тестовом сете
test_loss, test_acc = model_AlexNet.evaluate(X_test_processed, y_test_encoded, verbose=0)
print(f"Test loss: {test_loss:.4f}")
print(f"Test accuracy: {test_acc:.4f}")

# 3. Генерируем предсказания (вероятности) и метки
y_pred_prob = model_AlexNet.predict(X_test_processed)
y_pred = np.argmax(y_pred_prob, axis=1)

# 4. Classification report
print("\nClassification Report:")
print(classification_report(y_test_encoded, y_pred, target_names=label_encoder.classes_))

# 5. Confusion Matrix
cm = confusion_matrix(y_test_encoded, y_pred)
plt.figure(figsize=(6,5))
sns.heatmap(cm, annot=True, fmt="d", 
            xticklabels=label_encoder.classes_, 
            yticklabels=label_encoder.classes_,
            cmap="Blues")
plt.xlabel("Predicted")
plt.ylabel("True")
plt.title("Confusion Matrix")
plt.show()
Test loss: 1.0335
Test accuracy: 0.6875
WARNING:tensorflow:6 out of the last 7 calls to <function TensorFlowTrainer.make_predict_function.<locals>.one_step_on_data_distributed at 0x0000021BF5737F60> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has reduce_retracing=True option that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/guide/function#controlling_retracing and https://www.tensorflow.org/api_docs/python/tf/function for  more details.
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 174ms/step

Classification Report:
              precision    recall  f1-score   support

         cat       0.80      0.73      0.76        11
        lion       0.50      0.60      0.55         5

    accuracy                           0.69        16
   macro avg       0.65      0.66      0.65        16
weighted avg       0.71      0.69      0.69        16

No description has been provided for this image
In [38]:
import matplotlib.pyplot as plt

# 6. Рандомно берём 8 картинок из тестового сета
idxs = np.random.choice(len(X_test_processed), size=8, replace=False)
plt.figure(figsize=(12,6))
for i, idx in enumerate(idxs):
    img = X_test_processed[idx].reshape(128,128)  # single channel
    true_label = label_encoder.inverse_transform([y_test_encoded[idx]])[0]
    pred_label = label_encoder.inverse_transform([y_pred[idx]])[0]
    
    plt.subplot(2,4,i+1)
    plt.imshow(img, cmap='gray')
    plt.title(f"True: {true_label}\nPred: {pred_label}")
    plt.axis('off')
plt.tight_layout()
plt.show()
No description has been provided for this image