6.6 MiB
Лаб 12. Глубокие сверточные нейронные сети¶
Сначала поработаем с документами. Загрузим, почистит, классифицируем и воспользуемся глубоким обучением
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}")
Предобработка текстов
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())
Нормализация текста (Лемматизация)
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())
Фильтрация текста
# Функция фильтрации текста
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())
Векторизация
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)
Разделение на выборки
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
import jax
print(jax.__version__)
import keras
print(keras.__version__)
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
)
Теперь обучаю модель классификации
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'])
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_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()
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)
)
Архитектуры глубокой рекуррентной нейронной сети LSTM
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()
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)
)
lstm_model.evaluate(X_test, y_test)
Визуализация распределения вероятностей результатов модели на валидационной выборке
plt.hist(lstm_model.predict(X_test))
_ = plt.axvline(x=0.5, color="orange")
теперь сравним модели
# Сравнение моделей
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}')
# Построение диаграмм производительности моделей
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() # Отображаем график
Все 3 модели показади одинаково высокие результаты и показатели метрик точности и F1-оценки.
Выведим результат класторизации документов
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)
# Визуализация
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()
Тепрь к анализу изображений. Будет использовать те же, что и в лаба 9 и 11¶
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)
Сейчас преобработаем изображения
Изменим размер, преобразуем в оттенок серого и гистограмму
# Функция предобработки изображений
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)
Аугментируем наши данные, чтобы увеличить размер входных данных
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)
кодировка меток
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
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()
обучаем модель
# Обучение модели
history = model_AlexNet.fit(augmented_images, y_train_encoded, epochs=20, batch_size=32, validation_data=(X_test_processed, label_encoder.transform(y_test)))
оценка результат
# Визуализация процесса обучения
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()
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()
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()