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 — это «неизвестность», которую я должен преодолеть для своих достижений, а также трансформационный момент, через который я должен пройти, чтобы совершить что-то великое. Я считаю, что неопределенность и возможность — это две стороны одной медали.