Введение
Это часть серии постов блога, связанных с внедрением искусственного интеллекта. Если вам интересна предыстория этой истории или то, как она происходит:
Ссылки на предыдущие записи блога |
---|
Как соскрести результаты Google Local Results с помощью искусственного интеллекта |
Реальный пример машинного обучения на Rails |
Советы и сравнения по обучению искусственному интеллекту |
Машинное обучение в скраппинге с помощью Rails |
Реализация моделей ONNX в Rails |
Как гибридный парсер ML побеждает традиционный парсер |
Как провести бенчмаркинг реализаций ML на Rails |
Исследование методов машинного обучения для улучшения спецификационных тестов |
Исследование методов машинного обучения для улучшения спецификационных тестов – II |
Исследование методов машинного обучения для улучшения тестов спецификаций – III |
Исследование методов машинного обучения для улучшения спецификационных тестов – IV |
Как обучить масштабируемый классификатор с помощью FastAPI и SerpApi? |
На этой неделе мы продемонстрируем хранение и повторное использование обученных весов, а также расскажем о решающей ошибке, которую я допустил при расчете ранних результатов модели. Для сбора данных мы будем использовать SerpApi’s Google Organic Results Scraper API. Также вы можете ознакомиться с более подробным представлением данных, которые мы будем использовать, на игровой площадке.
Решающая ошибка, которую я допустил при расчете точности модели
Позвольте мне сначала рассказать о том, как я проводил расчеты на учебных примерах:
true_examples = key_array.map {|el| el = el.first == "1" ? el.second : nil}.compact
false_examples = key_array.map {|el| el = el.first == "0" ? el.second : nil}.compact
predictions = []
false_examples.each do |example|
prediction = test example, 2, vector_array, key_array
predictions << prediction
end
predictions.map! {|el| el = el == 1 ? 0 : 1}
true_examples.each_with_index do |example, index|
puts "--------------"
prediction = test example, 2, vector_array, key_array
predictions << prediction
puts "Progress #{(index.to_f/true_examples.size.to_f).to_f}"
puts "--------------"
end
prediction_train_accuracy = predictions.sum.to_f / predictions.size.to_f
puts "Prediction Accuracy for Training Set is: #{prediction_train_accuracy}"
Мы берем примеры, которые являются сниппетами, и примеры, которые не являются сниппетами, и запускаем их в обученной модели. Если предсказание примеров без сниппета окажется равным 0, то они будут засчитаны как правильные предсказания, а если результаты со сниппетом будут засчитаны как 1, то они также будут засчитаны как правильные результаты. В итоге результат скорости правильного предсказания составил 0,8187793427230047. Позже я использовал более крупный набор данных, и результат составил около 89%.
Вот логическое заблуждение, на которое я попался; Было исчерпывающе больше результатов без сниппетов по сравнению с результатами со сниппетами. Предположим, что это соотношение составляет 1:9 в наборе примеров из 10 единиц. Если модель склонна называть вещи non-snippet чаще, чем snippet в случайном порядке, как это было в данном случае, то результаты non-snippet будут предсказаны правильно, что создаст смещение в результатах.
Причина, по которой этот показатель увеличился до 89 процентов в более крупном наборе данных, заключается в том, что в нем было больше типов ключей. Это привело к тому, что соотношение размера сниппета и размера не сниппета еще больше уменьшилось, что вызвало перекос в ложноположительных предсказаниях результатов, не относящихся к сниппетам.
В действительности мне следовало протестировать модель, используя только примеры сниппетов. Я так и сделал и обнаружил, что модель бесполезна. Я постараюсь подправить модель и найти способ сделать ее полезной. Но я думаю, что лучше показать, почему я сделал неудачный расчет до этого, чтобы проинформировать всех и не дать другим людям совершить ту же ошибку.
Хранение обученных весов пользовательским способом
Мне потребовалось некоторое время, прежде чем я осознал свою ошибку. До этого я создал способ хранения и прогнозирования с помощью модели.
Вот полный код для этого:
class Predict
def initialize csv_path, trained_weights_path, vocab_path, object = "Snippet", k = 2
@@csv_path = csv_path
@@trained_weights_path = trained_weights_path
@@vocab_path = vocab_path
@@object = object
@@k = k
@@key_arr = []
@@vector_arr = []
@@weights = []
@@maximum_word_size = 0
@@vocab = {}
end
def self.construct
@@weights = initialize_trained_weights @@trained_weights_path
@@vocab = read_vocab @@vocab_path
@@key_arr = read_csv @@csv_path
@@vector_arr = define_training_set @@key_arr
@@maximum_word_size = @@weights.size
extend_vectors
end
def self.read_csv csv_path
CSV.read(csv_path)
end
def self.read_vocab vocab_path
vocab = File.read vocab_path
JSON.parse(vocab)
end
def self.initialize_trained_weights trained_weights_path
weights = File.read trained_weights_path
weights = JSON.parse(weights)
Vector.[](*weights)
end
def self.define_training_set vectors
@@key_arr.map { |word| word_to_tensor word[1] }
end
def self.default_dictionary_hash
{
/"/ => "",
/'/ => " ' ",
/./ => " . ",
/,/ => ", ",
/!/ => " ! ",
/?/ => " ? ",
/;/ => " ",
/:/ => " ",
/(/ => " ( ",
/)/ => " ) ",
/// => " / ",
/s+/ => " ",
/<br />/ => " , ",
/http/ => "http",
/https/ => " https ",
}
end
def self.tokenizer word, dictionary_hash = default_dictionary_hash
word = word.downcase
dictionary_hash.keys.each do |key|
word.sub!(key, dictionary_hash[key])
end
word.split
end
def self.word_to_tensor word
token_list = tokenizer word
token_list.map {|token| @@vocab[token]}
end
def self.extend_vector vector
vector_arr = vector.to_a
(@@maximum_word_size - vector.size).times { vector_arr << 1 }
Vector.[](*vector_arr)
end
def self.extend_vectors
@@vector_arr.each_with_index do |vector, index|
@@vector_arr[index] = extend_vector vector
end
end
def self.product vector
@@weights.each_with_index do |weight, index|
vector[index] = weight * vector[index]
end
vector
end
def self.euclidean_distance vector_1, vector_2
subtractions = (vector_1 - vector_2).to_a
subtractions.map! {|sub| sub = sub*sub }
Math.sqrt(subtractions.sum)
end
def self.execute example
example_vector = word_to_tensor example
example_vector.map! {|el| el = el.nil? ? 0: el}
example_vector = extend_vector example_vector
weighted_example = product example_vector
distances = []
@@vector_arr.each_with_index do |comparison_vector, vector_index|
distances << euclidean_distance(comparison_vector, weighted_example)
end
indexes = []
@@k.times do
index = distances.index(distances.min)
indexes << index
distances[index] = 1000000000
end
predictions = []
indexes.each do |index|
predictions << @@key_arr[index].first.to_i
end
puts "Predictions: #{predictions}"
prediction = (predictions.sum/predictions.size).to_f
if prediction < 0.5
puts "False - Item is not #{@@object}"
return 0
else
puts "True - Item is #{@@object}"
return 1
end
end
end
csv_path = "organic_results/organic_results__snippet.csv"
trained_weights_path = "organic_results/snippet_weights.json"
vocab_path = "organic_results/vocab.json"
Predict.new csv_path, trained_weights_path, vocab_path, object = "Snippet", k = 5
Predict.construct
true_examples = CSV.read(csv_path)
true_examples = true_examples.map {|el| el = el.first == "1" ? el.second : nil}.compact
true_examples.each_with_index do |example, index|
puts "--------"
puts "#{index}"
Predict.execute example
puts "--------"
end
Заключение
Я сделал честную попытку в области, которая могла бы облегчить написание тестов, и потерпел неудачу. Тонкие ошибки в пользовательских кодах могут привести к общей неудаче. Я благодарен за то, что попробовал и увидел, что этот подход не так эффективен, как я думал. Я буду продолжать работать над этим в будущем. Однако для следующей записи в блоге тема, скорее всего, будет другой. Я хотел бы извиниться перед читателями за предоставление недостоверных результатов в предыдущей записи блога и поблагодарить их за внимание.
Первоначально опубликовано на сайте https://serpapi.com 4 мая 2022 года.