Journey (Job and Empowerment for Disability) использует модель TensorFlow для фильтрации на основе контента, чтобы рекомендовать пользователям списки вакансий. 👩‍💼 Модель учится на атрибутах работы, таких как тип инвалидности, должности и навыки, для создания вложений, отражающих сходство между работами. 📚

Контентная фильтрация — это тип рекомендательной системы, которая работает с данными, которые предоставляет пользователь либо явно (рейтинг), либо неявно (нажатие на ссылку). Он использует данные для построения модели предпочтений пользователя, а затем рекомендует элементы, похожие на те, которым пользователь отдавал предпочтение в прошлом. 📊

Этот репозиторий содержит код для настройки гиперпараметров модели глубокого обучения с использованием TensorFlow и Keras. Модель обучена прогнозировать позиции на основе различных атрибутов, таких как тип инвалидности, навыки и должность. Настройка гиперпараметров выполняется с помощью библиотеки Kerastuner. 🧪

Набор данных

Данные, используемые для обучения и тестирования нашей модели, содержатся в файле CSV с именем Dataset_Disabilitas_500.csv 📝 Файл включает четыре различных атрибута: disabled_type, skills_one, skills_two и position. Мы разделили набор данных на обучающий и тестовый наборы в соотношении 80:20, чтобы обеспечить беспристрастную оценку модели. 🏋️‍♀️

Требование

Для запуска кода в этом репозитории необходимы следующие зависимости:

  1. ТензорФлоу
  2. NumPy
  3. научное обучение
  4. Керас Тюнер

Архитектура модели

Архитектура модели определяется в функции build_model. Он состоит из слоя внедрения, слоя LSTM и плотного выходного слоя с активацией softmax. Гиперпараметры модели, такие как размер встраивания, единицы LSTM и скорость обучения, настраиваются с помощью случайного поиска с библиотекой Kerastuner. 🧬

Настройка гиперпараметров

Настройка гиперпараметров выполняется с помощью тюнера RandomSearch. Он ищет лучшие гиперпараметры, оценивая несколько испытаний модели с различными конфигурациями гиперпараметров. Тюнер ищет оптимальные значения размера встраивания, единиц LSTM и скорости обучения. Затем лучшие гиперпараметры используются для построения окончательной модели. 🎯

Обучение

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

import tensorflow as tf
import numpy as np
import csv
from sklearn.model_selection import train_test_split

from tensorflow import keras
from tensorflow.keras.layers import Embedding, LSTM, Dense
from tensorflow.keras.models import Sequential
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences

filename = "D:\Bangkit_2023\Capstone_Project\Journey_Project\journey_project\dataset\Dataset_Disabilitas_500.csv"

attributes = ["disability_type", "skills_one", "skills_two", "position"]  

data = []

with open(filename, newline='') as csvfile:
    reader = csv.DictReader(csvfile)
    for row in reader:
        item = {}
        for attribute in attributes:
            item[attribute] = row[attribute]
        data.append(item)

# Split the data into training set and test set
train_data, test_data = train_test_split(data, test_size=0.2, random_state=42)  

# Print the training set
print("Training set:")
for item in train_data:
    print(item)

# Print the test set
print("Test set:")
for item in test_data:
    print(item)

# Extract the text features from the data
train_text = np.array([data['disability_type'] + ' ' + data['skills_one'] + ' ' + data['skills_two'] + ' ' + data['position'] for data in train_data])
test_text = np.array([data['disability_type'] + ' ' + data['skills_one'] + ' ' + data['skills_two'] + ' ' + data['position'] for data in test_data])

# Convert the labels to numerical values
train_labels = [data['position'] for data in train_data]
test_labels = [data['position'] for data in test_data]

# Convert the labels to one-hot encoded vectors
unique_labels = list(set(train_labels + test_labels))
num_labels = len(unique_labels)

label_mapping = {label: i for i, label in enumerate(unique_labels)}
train_labels_encoded = [label_mapping[label] for label in train_labels]
test_labels_encoded = [label_mapping[label] for label in test_labels]

train_labels_encoded = np.array(train_labels_encoded, dtype=np.int32)
test_labels_encoded = np.array(test_labels_encoded, dtype=np.int32)

train_labels_one_hot = tf.keras.utils.to_categorical(train_labels_encoded, num_labels)
test_labels_one_hot = tf.keras.utils.to_categorical(test_labels_encoded, num_labels)

# Tokenize the text data
tokenizer = Tokenizer()
tokenizer.fit_on_texts(train_text)

train_sequences = tokenizer.texts_to_sequences(train_text)
test_sequences = tokenizer.texts_to_sequences(test_text)

# Pad sequences to ensure equal length
max_seq_length = 100  
train_sequences = pad_sequences(train_sequences, maxlen=max_seq_length)
test_sequences = pad_sequences(test_sequences, maxlen=max_seq_length)

# Hyperparameter tuning
from tensorflow.keras import layers
from kerastuner.tuners import RandomSearch

def build_model(hp):
    model = keras.Sequential()
    model.add(layers.Embedding(input_dim=len(tokenizer.word_index) + 1, 
                               output_dim=hp.Int('embedding_dim', min_value=32, max_value=512, step=32),
                               input_length=max_seq_length))
    model.add(layers.LSTM(hp.Int('lstm_units', min_value=32, max_value=512, step=32)))
    model.add(layers.Dense(num_labels, activation='softmax'))
    model.compile(
        optimizer=keras.optimizers.Adam(hp.Choice('learning_rate', values=[1e-2, 1e-3, 1e-4])),
        loss='categorical_crossentropy',
        metrics=['accuracy'])
    return model

tuner = RandomSearch(
    build_model,
    objective='val_accuracy',
    max_trials=5,  # number of different hyperparameter combinations to try
    executions_per_trial=3,  # number of times to train each model, to average out the validation accuracy
    directory='D:\Bangkit_2023\Capstone_Project\Journey_Project\journey_project',  # directory to store the logs and trained models
    project_name='journey_hyperparameter_tuning')  

# Run the hyperparameter search
tuner.search(train_sequences, train_labels_one_hot,
             epochs=10,
             validation_data=(test_sequences, test_labels_one_hot))

# Get the optimal hyperparameters
best_hps=tuner.get_best_hyperparameters(num_trials=1)[0]

print(f"Optimal Embedding Dimension: {best_hps.get('embedding_dim')}")
print(f"Optimal LSTM Units: {best_hps.get('lstm_units')}")
print(f"Optimal Learning Rate: {best_hps.get('learning_rate')}")

# Define the early stopping criteria
from tensorflow.keras.callbacks import EarlyStopping
early_stopping = EarlyStopping(
    monitor='val_loss',  # Monitor validation loss
    min_delta=0.001,  # Minimum change to consider as improvement
    patience=5,  # Number of epochs with no improvement to stop training
    restore_best_weights=True,  # Restore the best weights recorded during training
)

# Define the model architecture
embedding_dim = 320  # Define the dimensionality of the word embeddings

model = Sequential()
model.add(Embedding(input_dim=len(tokenizer.word_index) + 1, output_dim=embedding_dim, input_length=max_seq_length))
model.add(LSTM(224))
model.add(Dense(num_labels, activation='softmax'))

# Compile the model
from tensorflow.keras.optimizers import Adam
optimizer = Adam(learning_rate=0.01)
model.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=['accuracy'])

# Train the model
batch_size = 32
epochs = 20

model.fit(train_sequences, train_labels_one_hot, batch_size=batch_size, epochs=epochs, validation_data=(test_sequences, test_labels_one_hot), callbacks=[early_stopping])

# Evaluate the model
loss, accuracy = model.evaluate(test_sequences, test_labels_one_hot)
print(f'Test Loss: {loss:.4f}')
print(f'Test Accuracy: {accuracy:.4f}')

# Recommender System
import requests
import json

# Set the input data
input_data = {
  "signature_name": "serving_default",
  "instances": [
    {
      "disability_type": "Hearing Impairment",
      "skills_one": "Adobe Illustrator",
      "skills_two": "UI UX Design",
      "position": "Unknown"
    }
  ]
}

new_text = [input_data['instances'][0]['disability_type'] + ' ' + input_data['instances'][0]['skills_one'] + ' ' + input_data['instances'][0]['skills_two'] + ' ' + input_data['instances'][0]['position']]

new_sequences = tokenizer.texts_to_sequences(new_text)
new_sequences = pad_sequences(new_sequences, maxlen=max_seq_length)

predictions = model.predict(new_sequences)
predicted_label_indices = np.argsort(predictions[0])[::-1][:10]
ranked_labels = [unique_labels[index] for index in predicted_label_indices]

# Sequential output according to rank
output = "[" + ", ".join(ranked_labels) + "]"
print(output)

Оценка

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

Test Loss: 1.0059 
Test Accuracy: 0.8929

Сохранение модели и развертывание

Предоставленный код объединяет Flask и TensorFlow Serving для создания веб-API для обслуживания прогнозов с использованием обученной модели TensorFlow. Flask обрабатывает входящие запросы POST, проверяет данные и выполняет их предварительную обработку. TensorFlow Serving размещает модель и выполняет прогнозы. Код загружает токенизатор, модель и уникальные метки во время инициализации. Конечная точка /predict предварительно обрабатывает входные данные, передает их модели для прогнозирования и возвращает ранжированные метки в виде ответа JSON. Чтобы развернуть код с помощью TensorFlow Serving, необходимо экспортировать модель в формате SavedModel, а также установить и настроить TensorFlow Serving для обслуживания модели. 🚀

Сохраненная модель

"""TF Serving_SavedModel.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1X92A2VRR7Vs8SbuIdaCW3Y2IU4Cg09b7
"""

pip install autokeras

import tensorflow as tf
import numpy as np
import csv
from sklearn.model_selection import train_test_split
import pickle

from tensorflow import keras
from tensorflow.keras.layers import Embedding, LSTM, Dense
from tensorflow.keras.models import Sequential
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences

filename = "/content/sample_data/Dataset_Disabilitas_500.csv"

attributes = ["disability_type", "skills_one", "skills_two", "position"]

data = []

with open(filename, newline='') as csvfile:
    reader = csv.DictReader(csvfile)
    for row in reader:
        item = {}
        for attribute in attributes:
            item[attribute] = row[attribute]
        data.append(item)

# Split the data into training set and test set
train_data, test_data = train_test_split(data, test_size=0.2, random_state=42)

# Print the training set
print("Training set:")
for item in train_data:
    print(item)

# Print the test set
print("Test set:")
for item in test_data:
    print(item)

# Extract the text features from the data
train_text = np.array([data['disability_type'] + ' ' + data['skills_one'] + ' ' + data['skills_two'] + ' ' + data['position'] for data in train_data])
test_text = np.array([data['disability_type'] + ' ' + data['skills_one'] + ' ' + data['skills_two'] + ' ' + data['position'] for data in test_data])

# Convert the labels to numerical values
train_labels = [data['position'] for data in train_data]
test_labels = [data['position'] for data in test_data]

# Convert the labels to one-hot encoded vectors
unique_labels = list(set(train_labels + test_labels))
num_labels = len(unique_labels)

label_mapping = {label: i for i, label in enumerate(unique_labels)}
train_labels_encoded = [label_mapping[label] for label in train_labels]
test_labels_encoded = [label_mapping[label] for label in test_labels]

train_labels_encoded = np.array(train_labels_encoded, dtype=np.int32)
test_labels_encoded = np.array(test_labels_encoded, dtype=np.int32)

train_labels_one_hot = tf.keras.utils.to_categorical(train_labels_encoded, num_labels)
test_labels_one_hot = tf.keras.utils.to_categorical(test_labels_encoded, num_labels)

# Tokenize the text data
tokenizer = Tokenizer()
tokenizer.fit_on_texts(train_text)

train_sequences = tokenizer.texts_to_sequences(train_text)
test_sequences = tokenizer.texts_to_sequences(test_text)

# Save the tokenizer
with open('tokenizer.pkl', 'wb') as tokenizer_file:
    pickle.dump(tokenizer, tokenizer_file)

# Pad sequences to ensure equal length
max_seq_length = 100
train_sequences = pad_sequences(train_sequences, maxlen=max_seq_length)
test_sequences = pad_sequences(test_sequences, maxlen=max_seq_length)

# Define the early stopping criteria
from tensorflow.keras.callbacks import EarlyStopping
early_stopping = EarlyStopping(
    monitor='val_loss',  # Monitor validation loss
    min_delta=0.001,  # Minimum change to consider as improvement
    patience=5,  # Number of epochs with no improvement to stop training
    restore_best_weights=True,  # Restore the best weights recorded during training
)

# Define the model architecture
embedding_dim = 320  # Define the dimensionality of the word embeddings

model = Sequential()
model.add(Embedding(input_dim=len(tokenizer.word_index) + 1, output_dim=embedding_dim, input_length=max_seq_length))
model.add(LSTM(224))
model.add(Dense(num_labels, activation='softmax'))

# Compile the model
from tensorflow.keras.optimizers import Adam
optimizer = Adam(learning_rate=0.01)
model.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=['accuracy'])

# Train the model
batch_size = 32
epochs = 20

model.fit(train_sequences, train_labels_one_hot, batch_size=batch_size, epochs=epochs, validation_data=(test_sequences, test_labels_one_hot), callbacks=[early_stopping])

# Evaluate the model
loss, accuracy = model.evaluate(test_sequences, test_labels_one_hot)
print(f'Test Loss: {loss:.4f}')
print(f'Test Accuracy: {accuracy:.4f}')

# Load the tokenizer
with open('tokenizer.pkl', 'rb') as tokenizer_file:
    tokenizer = pickle.load(tokenizer_file)

# Recommender System
import requests
import json

# Set the input data
input_data = {
  "signature_name": "serving_default",
  "instances": [
    {
      "disability_type": "Hearing Impairment",
      "skills_one": "Adobe Illustrator",
      "skills_two": "UI UX Design",
      "position": "Unknown"
    }
  ]
}

new_text = [input_data['instances'][0]['disability_type'] + ' ' + input_data['instances'][0]['skills_one'] + ' ' + input_data['instances'][0]['skills_two'] + ' ' + input_data['instances'][0]['position']]

new_sequences = tokenizer.texts_to_sequences(new_text)
new_sequences = pad_sequences(new_sequences, maxlen=max_seq_length)

import csv

# Save unique labels to a CSV file
with open('unique_labels.csv', 'w', newline='') as csvfile:
    writer = csv.writer(csvfile)
    writer.writerow(['label'])
    for label in unique_labels:
        writer.writerow([label])

import os

# Save the model in TensorFlow's SavedModel format
model.save('/content/sample_data/my_model')
import tensorflow as tf


# If you want to save it in a specific version, use this:
version = 1
export_path = os.path.join('/content/sample_data/my_model', str(version))
print('export_path = {}\n'.format(export_path))

tf.keras.models.save_model(
    model,
    export_path,
    overwrite=True,
    include_optimizer=True,
    save_format=None,
    signatures=None,
    options=None
)

print('\nSaved model:')
!ls -l {export_path}
# Load the SavedModel
loaded_model = tf.saved_model.load('/content/sample_data/my_model')

# Convert the model to a callable function
model_fn = loaded_model.signatures['serving_default']

# Assuming `new_sequences` is your input data
input_data = tf.convert_to_tensor(new_sequences, dtype=tf.float32)

# Make predictions using the model function
predictions = model_fn(input_data)

# Access the prediction output
output = predictions['dense']

# Assuming `new_sequences` is your input data
input_data = tf.convert_to_tensor(new_sequences, dtype=tf.float32)
predictions = loaded_model(input_data)

# Now you can use `predictions` as you did before

predictions = model.predict(new_sequences)
predicted_label_indices = np.argsort(predictions[0])[::-1][:10]
ranked_labels = [unique_labels[index] for index in predicted_label_indices]

# Sequential output according to rank
output = "[" + ", ".join(ranked_labels) + "]"
print(output)

from google.colab import files

# Specify the path of the SavedModel directory
saved_model_path = "/content/sample_data/my_model"

# Create a zip file of the SavedModel directory
!zip -r saved_model.zip {saved_model_path}

# Download the zip file
files.download("saved_model.zip")

Развертывание

from flask import Flask, request, jsonify
import tensorflow as tf
from tensorflow.keras.preprocessing.sequence import pad_sequences
import numpy as np
import pickle
import logging
import pandas as pd
import os

app = Flask(__name__)

# Update logging configuration
logging.basicConfig(format='%(asctime)s %(levelname)-8s [%(filename)s:%(lineno)d] %(message)s', 
                    level=logging.INFO, 
                    datefmt='%Y-%m-%d %H:%M:%S')

# Load from environment variables
tokenizer_path = os.getenv('TOKENIZER_PATH', 'tokenizer.pkl')
model_path = os.getenv('MODEL_PATH')
unique_labels_path = os.getenv('UNIQUE_LABELS_PATH', 'unique_labels.csv')

max_seq_length = 100

# Load the tokenizer, model and unique_labels at start
logging.info('Loading tokenizer...')
with open(tokenizer_path, 'rb') as f:
    tokenizer = pickle.load(f)

logging.info('Loading model...')
model = tf.keras.models.load_model(model_path)

# Load dataset and extract unique labels (positions)
logging.info('Loading dataset...')
df = pd.read_csv(unique_labels_path)
unique_labels = df['label'].unique().tolist()

def preprocess_input(skill_one, skill_two, id_disability):
    text = f'{id_disability} {skill_one} {skill_two}'
    sequences = tokenizer.texts_to_sequences([text])
    sequences = pad_sequences(sequences, maxlen=max_seq_length)
    return sequences

@app.route('/predict', methods=['POST'])
def predict():
    try:
        logging.info('Received prediction request...')
        data = request.get_json()

        # check if data is None
        if not data:
            logging.warning('No input data provided')
            return jsonify({'message': 'No input data provided'}), 400

        # Extract input features
        skill_one = data.get('skill_one')
        skill_two = data.get('skill_two')
        id_disability = data.get('id_disability')

        # check if fields are missing
        if any(arg is None for arg in [skill_one, skill_two, id_disability]):
            logging.warning('Data missing in JSON')
            return jsonify({'message': 'Data missing in JSON'}), 400

        logging.info('Preprocessing input...')
        sequences = preprocess_input(skill_one, skill_two, id_disability)

        logging.info('Making predictions...')
        predictions = model.predict(sequences)
        predicted_label_indices = np.argsort(predictions[0])[::-1][:10]
        ranked_labels = [unique_labels[index] for index in predicted_label_indices]

        logging.info('Preparing response...')
        response_data = {'predictions': ranked_labels}

        return jsonify(response_data)
    except Exception as e:
        logging.error(f'An error occurred: {str(e)}')
        return jsonify({'message': 'An error occurred'}), 500

if __name__ == '__main__':
    app.run(debug=True)

Результат пути

Journey — это программа, призванная помочь людям с ограниченными возможностями найти подходящую работу. Используя сложную систему, построенную на TensorFlow, он анализирует такие атрибуты работы, как тип инвалидности, соответствующие навыки и должности. На основе этих атрибутов он создает уникальный профиль для каждого задания. Думайте об этом как о сопоставлении кусочков головоломки: система учится на том, к чему пользователь проявил интерес, а затем предлагает вакансии, которые лучше всего соответствуют профилю этого пользователя. Этот расширенный процесс рекомендаций становится еще лучше благодаря постоянным корректировкам, поскольку программа точно настраивает свои прогнозы с помощью библиотеки Kerastuner, гарантируя, что рекомендации становятся все более точными и адаптированными для каждого человека.

Инновации, основанные на данных, формируют будущее с Qarir Generator!

В программе ученичества QarirGenerator у меня будет прекрасная возможность применить знания, которые я сейчас приобретаю, в реальных условиях. Это не только служит мостом для моего карьерного роста в области аналитика данных и специалиста по данным, но также позволяет мне глубоко понимать проблемы, с которыми сталкиваются в различных проектах, каждый со своим уникальным набором данных и характеристиками. Участвуя в различных проектах в Qgen, я верю, что этот опыт и знакомство отточат мои аналитические способности и подготовят меня к предоставлению более широких решений для людей. Для меня быть частью стажера-аналитика данных в Qarir Generator — это «неизвестность», которую я должен преодолеть для своих достижений, а также трансформационный момент, через который я должен пройти, чтобы совершить что-то великое. Я считаю, что неопределенность и возможность — это две стороны одной медали.