
Journey (Job and Empowerment for Disability) использует модель TensorFlow для фильтрации на основе контента, чтобы рекомендовать пользователям списки вакансий. 👩💼 Модель учится на атрибутах работы, таких как тип инвалидности, должности и навыки, для создания вложений, отражающих сходство между работами. 📚
Контентная фильтрация — это тип рекомендательной системы, которая работает с данными, которые предоставляет пользователь либо явно (рейтинг), либо неявно (нажатие на ссылку). Он использует данные для построения модели предпочтений пользователя, а затем рекомендует элементы, похожие на те, которым пользователь отдавал предпочтение в прошлом. 📊
Этот репозиторий содержит код для настройки гиперпараметров модели глубокого обучения с использованием TensorFlow и Keras. Модель обучена прогнозировать позиции на основе различных атрибутов, таких как тип инвалидности, навыки и должность. Настройка гиперпараметров выполняется с помощью библиотеки Kerastuner. 🧪
Набор данных
Данные, используемые для обучения и тестирования нашей модели, содержатся в файле CSV с именем Dataset_Disabilitas_500.csv 📝 Файл включает четыре различных атрибута: disabled_type, skills_one, skills_two и position. Мы разделили набор данных на обучающий и тестовый наборы в соотношении 80:20, чтобы обеспечить беспристрастную оценку модели. 🏋️♀️
Требование
Для запуска кода в этом репозитории необходимы следующие зависимости:
- ТензорФлоу
- NumPy
- научное обучение
- Керас Тюнер
Архитектура модели
Архитектура модели определяется в функции 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 — это «неизвестность», которую я должен преодолеть для своих достижений, а также трансформационный момент, через который я должен пройти, чтобы совершить что-то великое. Я считаю, что неопределенность и возможность — это две стороны одной медали.