Привет всем, это практическое руководство по увлекательной теме; сегодня мы обсудим, как вы можете работать с комбинацией смешанных данных. Ну, мы все прошли через это, когда просматриваем набор данных, и есть функции с разными типами данных, и мы задаемся вопросом, как мы можем объединить оба типа и использовать их для обучения одной модели машинного обучения. Что ж, сегодня у вас есть простой ответ на этот вопрос. Кроме того, чтобы сделать вещи интересными, мы будем обучать модель машинного обучения, которая предсказывает неприязни на YouTube в режиме реального времени. Уже неудивительно, что YouTube удалил свою функцию подсчета дизлайков год назад, может быть, я даже немного опоздал с решением этой проблемы, но набор данных, который мы используем сегодня, наверняка удовлетворит наши потребности в обучении на сегодня. Помните, что, поскольку неприязнь YouTube — это число, мы решаем проблему регрессии. 🙂

Оглавление

  1. "Загрузка данных"
  2. Предварительная обработка данных
  3. Моделирование и обучение
  4. "Оценка"
  5. Прогноз в реальном времени

Что вы узнаете

  • Работа со смешанными данными
  • Чистые текстовые данные
  • Обработка как текстовых, так и числовых данных
  • Функциональный API Keras TensorFlow
  • ЛСТМ (РНН)
  • Чтобы создать модель, которая обрабатывает разные типы данных одновременно
  • как проверить точность регрессионной модели
  • Ютуб API

Реальность: на YouTube количество просмотров, отметок "Нравится" и "Не нравится", которое может получить видео, зависит от множества факторов, таких как популярность создателя, качество видео, SEO, юзершеры и много других бедер, которые выходят за рамки доступного нам набора данных. В любом случае, давайте постараемся выжать максимум из того, что у нас есть 😁

Примечание. в этом руководстве я буду упоминать, что и как вы можете сделать, чтобы улучшить эту модель, а также то, что я упустил при ее создании, если я что-то упустил. И когда дело доходит до добавления библиотек, мы будем добавлять по мере необходимости, а не импортировать их все сразу

Начнем…

Вы можете получить набор данных на https://www.kaggle.com/datasets/dmitrynikolaev/youtube-dislikes-dataset

Загрузите набор данных и извлеките его в свой рабочий каталог. Этот набор данных представляет собой набор данных смешанного языка из 37422 уникальных необработанных данных, которые мы собираемся сократить до только английского языка, используя идентификаторы видео, предоставленные с набором данных, что делает наш набор данных в общей сложности 15835 уникальными необработанными данными перед обработкой. На самом деле это очень короткий объем данных для решения подобной проблемы, попробуйте увеличить объем данных, обрабатывая другие языки, что, несомненно, поможет повысить точность модели.

Примечание. Последний раз этот набор данных обновлялся 12.13.2021, поэтому мы предполагаем, что это дата извлечения этих данных, поскольку мы будем использовать эти данные для работы с периодом времени. эм>

Загрузить набор данных и собрать нужные нам функции

Надеюсь, у вас есть файл .csv в вашем рабочем каталоге, давайте загрузим его с помощью pandas, сократим до английского и получим более подробную информацию о доступных функциях из набора данных.

Импортируйте некоторые библиотеки, чтобы начать

import numpy as np
import pandas as pd
import tensorflow as tf

Загрузка набора данных

dataset = pd.read_csv('youtube_dislike_dataset.csv')
dataset.head()

Сбор данных только на английском языке

file_US_ids = open("video_IDs/unique_ids_US.txt", "r")
US_ids = file_US_ids.read().splitlines()
dataset = dataset[dataset['video_id'].isin(US_ids)]
dataset.shape 

Проверка дополнительных сведений о наборе данных, таких как типы данных объектов.

dataset.info()

Набор данных содержит 12 признаков. Мы будем использовать только 7 функций из них. Эти функции описаны ниже:

Video_id: уникальный идентификатор видео
Title: название видео на YouTube
Channel_id:идентификатор канала издателя
Channel_title: название канала
Published_at: дата публикации видео
View_count: количество просмотров этого видео за определенный период времени
Нравится: количество отметок "Нравится" этому видео
Не нравится: количество отметок "Не нравится" этому видео за определенный период времени
Comment_count: количество комментариев к видео
Теги: теги видео в виде строк
Описание: Описание видео
Комментарии: список комментариев к видео в виде строки

Итак, у вас есть все доступные функции, теперь давайте выберем некоторые из них, которые мы будем использовать в этом практическом руководстве.

Мы будем использовать следующие 7 функций:
количество_просмотров, опубликованное_в, лайки, количество комментариев, теги, описание, дизлайки

dataset = dataset[['view_count', 'published_at','likes', 'comment_count','tags','description','dislikes']]

Мы создадим еще одну функцию, а также избавимся от одной функции отсюда на этапе предварительной обработки. Честно говоря, использование заголовка и названия канала было бы отличным улучшением модели, можете попробовать. 🙂

Предварительная обработка данных

Давайте начнем с обработки данных, на этом этапе мы делаем много важных вещей с нашими наборами данных, таких как работа с пропущенными значениями, создание связанных полей времени, а также очистка и токенизация текстовых данных. Позвольте мне объяснить больше, делая это.

Работа с отсутствующими значениями

Набор данных довольно сложен, если вы просто ищете пропущенные значения (nan), вы ничего не найдете, потому что в некотором смысле в наборе данных нет пропущенных значений, поскольку все поля заполнены пустая строка, так что обработайте этот случай, мы проверим поля пустых строк, в которых нет слова, и преобразуем их в значение nan и проверим снова, я уверен, что мы получим хороший количество нулевых значений сейчас. И угадай что? Мы удалим их все, это сделает наш набор данных в общей сложности 13536 RAW, что довольно мало 😂

Создание новой функции — работа со временем

Выше я упомянул, что мы собираемся предположить, что данные были извлечены 12.13.2021, ну, это время, когда эта дата вступает в игру.

Мы собираемся создать новую функцию с именем timesec, рассчитав количество времени между датой публикации видео и временем его извлечения в минутах.

Таким образом, в основном: время в секундах = дата извлечения - дата публикации

Во-первых, мы напишем функцию для вычисления времени между двумя точками в минутах и ​​используем метод применения pandas для создания новой функции.

Функция для расчета времени между:

from datetime import datetime
def calTime(time):
  start = datetime.strptime(time, '%Y-%m-%d %H:%M:%S')
  end =   datetime.strptime('13/12/2021 00:00:00', '%d/%m/%Y %H:%M:%S') # assuming this is the date that this dataset was extracted
  return np.round((end - start).total_seconds() / 60, 2)

Использование метода применения pandas для запуска функции и создания новой функции

dataset['timesec'] = dataset['published_at'].apply(calTime)

Очистка текстовых элементов

Мы создадим функцию для очистки наших текстовых данных, а затем ряд шагов, которые помогут извлечь из них правильное и важное значение.

По сути, функция удалит все ненужные URL-адреса, знаки препинания, цифры, стоп-слова и случайные слова, которых нет в словаре. Он также будет иметь дело со словами-сокращениями, лемматизировать слова и, наконец, преобразовать все в нижний регистр и вернуть обработанную строку. Мы сделаем это с помощью нескольких библиотек, так что давайте начнем с их импорта.

Импорт необходимых библиотек для этого

import re
from string import punctuation 
from bs4 import BeautifulSoup
import nltk
nltk.download('stopwords')
nltk.download('wordnet')
nltk.download('omw-1.4')
nltk.download('words')

from nltk.corpus import stopwords
from nltk.stem import WordNetLemmatizer

Словарь сокращенных слов поможет преобразовать комбинацию двух слов в их отдельные корневые слова. (это не все, есть еще)

contraction_map={
    "ain't": "is not",
    "aren't": "are not",
    "can't": "cannot",
...
}

Функция, которую мы создали для очистки текста

lemmatizer = WordNetLemmatizer()
in_words = set(nltk.corpus.words.words())
def clean_text(text):
    text = str(text)
    text = BeautifulSoup(text, "lxml").text
    text = re.sub(r'\([^)]*\)', '', text)
    text = re.sub('"','', text)
    text = ' '.join([contraction_map[t] if t in contraction_map else t for t in text.split(" ")])
    text = re.sub(r"'s\b","",text)
    text = re.sub("[^a-zA-Z]", " ", text)
    text = " ".join(w for w in nltk.wordpunct_tokenize(text) if w.lower() in in_words or not w.isalpha())

    text = [word for word in text.split( ) if word not in stopwords.words('english')]
    text = [lemmatizer.lemmatize(word) for word in text]
    text = " ".join(text)
    text = text.lower()
    return text

Наконец, создание двух новых столбцов с очищенным текстом после запуска функции

%time dataset['clean_description'] = dataset['description'].apply(clean_text)
%time dataset['clean_tags'] = dataset['tags'].apply(clean_text)

Разделение данных

Поскольку мы используем разные типы данных, не идеально использовать обычную функцию train_test_split, поэтому мы начнем с разделения данных по поезду и проверим с помощью слайсинга, после чего мы отделим X и Y как от обучающих, так и от тестовых данных, затем разделим текстовые и числовые функции, мы сохраним и теги, и описание отдельно, но сохраним все числовые функции как одно целое. В любом случае это может сбивать с толку, давайте посмотрим на код, который прояснит ситуацию.

Примечание. Y – это признак неприязни и наша целевая переменная. И X будет view_count, likes, comment_count, timesec, clean_tags, clean_description. Нам не нужна функция publish_at.

Нарезка для обучения и тестирования

# Slicing to train and test
dataset_train = dataset.iloc[:12500,:]
dataset_test = dataset.iloc[12501:,:]

# Creating X and Y variables of train dataset
X_train = dataset_train.loc[:, dataset.columns != 'dislikes']
Y_train = dataset_train['dislikes'].values

#Creating X and Y variables of the test dataset
X_test = dataset_test.loc[:, dataset.columns != 'dislikes']
Y_test = dataset_test['dislikes'].values

# Splitting and organizing the features of train data
X_train_numaric = X_train[['view_count', 'likes', 'comment_count', 'timesec']].values
X_train_tags = X_train['clean_tags'].values
X_train_desc = X_train['clean_description'].values

# Splitting and organizing the features of the test data
X_test_numaric = X_test[['view_count', 'likes', 'comment_count', 'timesec']].values
X_test_tags = X_test['clean_tags'].values
X_test_desc = X_test['clean_description'].values

Токенизация текста — обработка текста

Здесь мы собираемся создать функцию для функции токенизации для обработки наших текстовых функций. Функция создаст токенизатор и токенизирует слова, добавит к нему отступ 100 и вернет набор полезной информации, такой как обработанные тестовые и обучающие данные, количество максимальных слов, словарный запас и его размер, а также токенизатор. сам, который мы будем использовать позже, чтобы делать прогнозы в реальном времени.
Нам придется использовать это как для тегов, так и для описаний.

Давайте перейдем к коду. 🙂

Импорт необходимых библиотек для этого

from tensorflow.keras.preprocessing.text import Tokenizer 
from tensorflow.keras.preprocessing.sequence import pad_sequences

Функция для обработки

def Tokenizer_func(train,test, max_words_length=0, max_seq_len=100):
    tokenizer = Tokenizer()
    tokenizer.fit_on_texts(train)
    
    max_words = 0
    if max_words_length > 0:
        max_words = max_words_length
    else:
        max_words = len(tokenizer.word_counts.items())

    tokenizer = Tokenizer(num_words=max_words)
    tokenizer.fit_on_texts(train)

    train_sequences = tokenizer.texts_to_sequences(train)
    test_sequences = tokenizer.texts_to_sequences(test)
    
    train = pad_sequences(train_sequences,maxlen=max_seq_len, padding='post')
    test = pad_sequences(test_sequences,maxlen=max_seq_len, padding='post')
    
    voc = tokenizer.num_words +1
    return {'train': train, 'test': test, 'voc': voc, 'max_words':max_words, 'tokenizer': tokenizer}

Вызов функции для тегов

X_tags_processed = Tokenizer_func(X_train_tags,X_test_tags)
# Extracting data from it…
X_train_tags,X_test_tags,x_tags_voc,x_tags_max_words,x_tag_tok = X_tags_processed['train'], X_tags_processed['test'],X_tags_processed['voc'],X_tags_processed['max_words'],X_tags_processed['tokenizer']

Вызов функции для описания

X_desc_processed = Tokenizer_func(X_train_desc,X_test_desc)
# Extracting data from it…
X_train_desc, X_test_desc,x_desc_voc,x_desc_max_words,x_desc_tok = X_desc_processed['train'], X_desc_processed['test'],X_desc_processed['voc'],X_desc_processed['max_words'],X_desc_processed['tokenizer']

Ну, у нас есть еще одна вещь, которую нужно сделать, прежде чем закончить этап обработки этого урока.

Нормализация числовых данных

Для этого мы будем использовать StandardScaler для нормализации как обучающих, так и тестовых данных. Что ж, давайте посмотрим на код.
Импорт библиотек, создание скейлера и обработка числовых данных с его помощью

from sklearn.preprocessing import StandardScaler
Sc = StandardScaler()
X_train_numaric = Sc.fit_transform(X_train_numaric)
X_test_numaric = Sc.transform(X_test_numaric)

Ура… мы наполовину закончили… это конец предварительной обработки данных. 🎉🎉🎉

Создание модели и обучение

Здесь мы будем использовать функциональный API Keras для создания модели машинного обучения. Мы создадим тип RNN, известный как LSTM, для обработки текстовых данных, а затем подключим его к MLP с числовыми данными. Я не буду объяснять LSTM здесь, так как я уже прошел через него в предыдущем посте, ссылка на который здесь…



Позвольте мне объяснить нашу модель здесь:

У нас есть 3 входа в модели: ввод тегов, ввод описания, который не имеет входной формы, и числовой размер ввода, равный 4, поскольку у нас есть 4 числовых признака. И теги, и описание начинаются со слоя внедрения, а затем с двух слоев LSTM с единицами измерения 100 (помните, что мы добавили 100 в качестве заполнения), за которыми следует слой нормализации после каждого LSTM позже. Для обеих функций первый уровень LSTM будет иметь отсев 0,2 и последовательность возврата, установленную как true, а второй уровень имеет отсев 0,4 и последовательность возврата, установленную как false. После прохождения через эти слои теги и описание объединяются с вводом числовых данных, а затем проходят через 4 плотных слоя с единицами измерения 256, 128, 32 и 1, первые 3 слоя с функцией активации relu и последний слой с линейной функцией активации (потому что проблема в регрессии). Затем вся модель скомпилирована с использованием метода model.compile(), заданного как mean_squared_error в качестве функции потерь и mean_absolute_error в качестве матрицы и adam в качестве оптимизатора, поскольку скорость обучения функций Adam уменьшилась до 0,001. Затем, наконец, мы можем просмотреть сводку модели, используя метод model.summary().

Давайте посмотрим код…

Для этого импортируйте необходимые библиотеки.

from keras.models import Model
from tensorflow.keras.layers import Input, LSTM, Embedding, Dense, concatenate,LayerNormalization
from keras.optimizers import Adam
from keras.callbacks import EarlyStopping

Создание модели

tagsInput = Input(shape=(None,), name='tags')
descInput = Input(shape=(None,), name='desc')
numaricInput = Input(shape=(4,), name='numaric')

tags = Embedding(input_dim=x_tags_voc,output_dim=8,input_length=x_tags_max_words)(tagsInput)
tags = LSTM(100,dropout=0.2, return_sequences=True)(tags)
tags = LayerNormalization()(tags)
tags = LSTM(100,dropout=0.4, return_sequences=False)(tags)
tags = LayerNormalization()(tags)

desc = Embedding(input_dim=x_desc_voc,output_dim=8,input_length=x_desc_max_words)(descInput)
desc = LSTM(100,dropout=0.2, return_sequences=True)(desc)
desc = LayerNormalization()(desc)
desc = LSTM(100,dropout=0.4, return_sequences=False)(desc)
desc = LayerNormalization()(desc)

combined = concatenate([tags, desc,numaricInput])
x = Dense(256,activation='relu')(combined)
x = Dense(128,activation='relu')(x)
x = Dense(32,activation='relu')(x)
x = Dense(1,use_bias=True,activation='linear')(x)
model = Model([tagsInput, descInput,numaricInput], x)

Компиляция модели

model.compile(loss='mean_squared_error', optimizer=Adam(learning_rate=0.001, decay=0.001 / 20), metrics=['mae'])

Теперь давайте посмотрим на сводку модели, используя метод model.sumary().

Теперь пришло время обучить нашу модель. Мы будем обучать нашу модель до 500 эпох с размером пакета 25 и с разделением проверки 2 процента, поскольку у нас есть ранняя остановка, вероятно, будет выполнено обучение модели до того, как она приблизится к 500 эпохам.

Что ж, есть еще одна вещь, которую мы должны сделать, прежде чем начать обучать нашу модель. LSTM поддерживает трехмерные данные, но наши данные двумерные, поэтому мы должны преобразовать их в трехмерные данные, прежде чем мы начнем обучение.

Инициализация EarlyStopping, чтобы избежать переобучения при обучении модели

es = EarlyStopping(monitor='val_loss', mode='min', verbose=1, patience=20)

Преобразование данных в трехмерные данные

X_train_tags = np.reshape(X_train_tags,(X_train_tags.shape[0],X_train_tags.shape[1],1))
X_train_desc = np.reshape(X_train_desc,(X_train_desc.shape[0],X_train_desc.shape[1],1))
X_train_tags.shape, X_train_desc.shape, X_train_numaric.shape

Обучение модели

history = model.fit(
                    x=[X_train_tags, X_train_desc,X_train_numaric],
                    y=Y_train,
                    epochs=500, 
                    batch_size=25,
                    validation_split=0.2,
                    verbose=1,
                    callbacks=[es]
                  )

Здесь мы видим, что наша модель прекратила обучение, как только достигла 26 эпох.

Оценка нашей модели

Теперь, когда наша модель прошла обучение, пришло время проверить результаты, но перед этим давайте ответим на простой вопрос и поймем пару важных вещей.
Как проверить точность регрессионной модели?

Остатки — разница между прогнозируемым значением и фактическим значением.

Средняя абсолютная ошибка — среднее значение невязок между прогнозируемыми и фактическими значениями.

  • Чем ниже MAE, тем лучше модель.
  • MAE рассчитывается путем добавления остатков и деления на длину набора данных (тестовые данные).
  • Описывает, насколько прогнозы в среднем близки к фактическим значениям.

Среднеквадратическая ошибка: показывает, насколько близка линия регрессии к набору точек данных.

Среднеквадратичная ошибка — это квадратный корень из среднего значения всех квадратов значений ошибок.

  • указывает на абсолютное соответствие модели относительно заданных данных
  • RMSE имеет отношение к MAE
  • Если значение RMSE намного выше, чем MAE, это означает, что в наборе данных есть какие-то большие ошибки.

R-squared – показывает, насколько данные соответствуют модели.

  • Выводит значение по шкале от 0 до 1
  • Чем выше R2, тем лучше модель.
  • Значение R2 можно использовать для получения точности модели, такой же, как и у модели классификации, путем умножения значения * 100.

Ключевые моменты:

  • R2 должен быть высоким
  • Чем ниже MSE, тем лучше модель.
  • Чем ниже MAE, тем лучше модель.
  • Чем ниже RMSE, тем лучше модель.
  • MAEдолжен быть ниже RMSE

Теперь давайте реализуем их в нашем проекте и посмотрим на результаты модели. Мы можем сделать это, используя следующий код

pprydd = model.predict([X_test_tags, X_test_desc, X_test_numaric])
from sklearn import metrics
print("Mean Absolute Error (MAE) - Test data : ", metrics.mean_absolute_error(Y_test, pprydd))
print("Mean Squared Error (MSE) - Test data : ", metrics.mean_squared_error(Y_test, pprydd))
print("Root Mean Squared Error (RMSE) - Test data : ", np.sqrt(metrics.mean_squared_error(Y_test, pprydd)))
print("Co-efficient of determination (R2 Score): ", metrics.r2_score(Y_test, pprydd))

Мы видим, что наша модель имеет оценку R2 0,82 (82%), но также мы видим, что у нас очень высокая MSE, что не является хорошим признаком 😥. И мы видим, что и MAE, и RMSE довольно низкие по сравнению с MSE, что хорошо, но RMSE выше, чем MAE, что на самом деле указывает на результат некоторых ошибок в нашем наборе данных 🤕… не лучшая модель в мире, но, на мой взгляд, это неплохо по сравнению с имеющимися у нас полезными данными.

Примечание: вы можете попробовать улучшить модель, проведя дополнительные исследования в этой области, например, от чего зависит получение большей неприязни к видео, и сосредоточьтесь на этом. Кроме того, попробуйте использовать функции, которые мы не использовали в этом уроке… в любом случае, продолжайте.

Прогноз в реальном времени

Теперь часть, которую мы все ждали, чтобы предсказать неприязнь YouTube в режиме реального времени. Для этого мы будем использовать API YouTube, чтобы получать целевые видеоданные по идентификатору, собирать из них необходимые данные, обрабатывать их и запускать через модель. Чтобы упростить задачу, мы напишем функцию, которая сделает все это сразу.
Давайте посмотрим на код. :)

Импортировать необходимую библиотеку

import googleapiclient.discovery

Создать ютуб клиент

DEVELOPER_KEY = 'YOUR_API_KEY' 
youtube_client = googleapiclient.discovery.build('youtube', 'v3', developerKey=DEVELOPER_KEY)

Время написать функцию. Поясню, как это работает.
Как только данные будут получены, мы извлечем из них необходимые поля.

Для обоих текстовых полей текст будет очищаться с помощью ранее созданной функции очистки, а затем будет обрабатываться созданным ранее токенизатором, и, наконец, к тексту будет добавлен отступ.

Поле timesec будет вычислено с помощью следующей функции. Внутри этой функции он получит период времени между датой публикации целевого видео и сегодняшним днем ​​в минутах.

Далее все числовые признаки будут пропущены через Scaler для нормализации. Как только обработка будет завершена, мы предскажем неприязнь к видео, пройдя через модель, и функция вернет предсказанное число неприязни среди некоторой другой полезной информации.

def realtime(youtube,video_id):
  def calTimesss(time):
    start = datetime.strptime(time, '%Y-%m-%dT%H:%M:%S%z')
    end =   datetime.now()
    return np.round((end - start.replace(tzinfo=end.tzinfo)).total_seconds() / 60, 2)
  request = youtube.videos().list(part="snippet, statistics",id=video_id)
  response = request.execute()

  desc = response['items'][0]['snippet']['description']
  desc  =[clean_text(desc)]
  desc = x_desc_tok.texts_to_sequences(desc)
  desc = pad_sequences(desc,maxlen=100, padding='post')
  
  tags = response['items'][0]['snippet']['tags']
  tags=(" ".join(tags))
  tags  =[clean_text(tags)]
  tags = x_tag_tok.texts_to_sequences(tags)
  tags = pad_sequences(tags,maxlen=100, padding='post')

  publishedAt = response['items'][0]['snippet']['publishedAt']
  timesec = calTimesss(publishedAt)
  viewcount = response['items'][0]['statistics']['viewCount']
  likeCount = response['items'][0]['statistics']['likeCount']
  commentCount = response['items'][0]['statistics']['commentCount']
  numaricdata = [[viewcount, likeCount,commentCount,timesec]]
  numaricdata = Sc.transform(numaricdata)

  pryd = model.predict([tags, desc, numaricdata])

  return {"predicted": int(pryd[0][0]), "info": {
      "video_id": video_id,
      "likes": likeCount,
      "commentCount": commentCount,
      "viewCount": viewcount,
      "publishedAt": publishedAt,
      "dislike": int(pryd[0][0]),
  }}

Теперь давайте запустим его и посмотрим на результаты…

video_id = "videoid"
realtime(youtube_client, video_id)

Ха-ха, сработало 😂 🎉. Согласно модели, у этого видео 1231 диск. Ой, много хейтеров 😟

Что ж, это конец этого руководства по работе с комбинированными данными, было весело, надеюсь, вам понравилось. Также надеюсь, что вы попытаетесь улучшить эту модель… Продолжайте учиться 🙂

ссылка на репозиторий GitHub: https://github.com/nafiu-dev/youtube_dislike_prediction_practice

Вы можете связаться со мной здесь:

https://www.instagram.com/nafiu.dev

Linkedin: https://www.linkedin.com/in/nafiu-nizar-93a16720b

Другие мои сообщения: