[실패] 사양 테스트 개선을 위한 기계 학습 기술 조사 — V

28138 단어

소개



이것은 인공 지능 구현과 관련된 블로그 게시물 시리즈의 일부입니다. 이야기의 배경이나 진행 방식에 관심이 있는 경우:


이전 블로그 게시물 링크


How to scrape Google Local Results with Artificial Intelligence

Real World Example of Machine Learning on Rails

AI Training Tips and Comparisons

Machine Learning in Scraping with Rails

Implementing ONNX models in Rails

How ML Hybrid Parser Beats Traditional Parser

How to Benchmark ML Implementations on Rails

Investigating Machine Learning Techniques to Improve Spec Tests

Investigating Machine Learning Techniques to Improve Spec Tests - II

Investigating Machine Learning Techniques to Improve Spec Tests - III

Investigating Machine Learning Techniques to Improve Spec Tests - IV

How to Train a Scalable Classifier with FastAPI and SerpApi ?


이번 주에는 훈련된 가중치 저장 및 재사용과 모델의 초기 결과를 계산할 때 저지른 중대한 실수에 대해 이야기할 것입니다. 데이터 수집을 위해 SerpApi’s Google Organic Results Scraper API을 사용할 것입니다. 또한 playground에서 사용할 데이터에 대한 자세한 내용을 확인할 수 있습니다.



모델 정확도를 계산할 때 저지른 중대한 실수



먼저 교육 예제를 사용하여 계산한 방법을 알려드리겠습니다.

    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%였습니다.

여기에 내가 넘어진 논리적 오류가 있습니다. 스니펫 결과와 달리 스니펫이 아닌 결과가 훨씬 더 많았습니다. 10 단위 예제 세트에서 비율이 1:9라고 가정해 봅시다. 모델이 임의의 방식으로 스니펫보다 스니펫이 아닌 것을 호출하는 경향이 있는 경우 스니펫이 아닌 결과가 올바르게 예측되어 결과에 편향이 생깁니다.

더 큰 데이터 세트에서 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


결론



나는 테스트 작성에 대한 부담을 어느 정도 덜어줄 수 있는 영역에 정직하게 시도했고 실패했습니다. 사용자 정의 코드에 미묘한 실수가 발생할 수 있으며 전체 실패로 이어질 수 있습니다. 한 번 시도한 것에 감사하고 이 접근 방식이 생각만큼 효과적이지 않다는 것을 알게 되었습니다. 나는 앞으로 이것에 대해 더 노력할 것입니다. 그러나 다음 블로그 게시물의 경우 주제가 다를 가능성이 큽니다. 이전 블로그 게시물에서 오해의 소지가 있는 결과를 제공한 점에 대해 독자 여러분께 사과의 말씀을 드리며, 관심을 가져주신 점에 감사드립니다.

2022년 5월 4일 https://serpapi.com에 처음 게시되었습니다.

좋은 웹페이지 즐겨찾기