JavaRush /Blog Java /Random-VI /Học máy dành cho nhà phát triển Java, Phần 2

Học máy dành cho nhà phát triển Java, Phần 2

Xuất bản trong nhóm
Học máy dành cho nhà phát triển Java, Phần 1
Học máy dành cho nhà phát triển Java, Phần 2 - 1

Ước lượng hàm mục tiêu

Chúng ta hãy nhớ lại rằng hàm mục tiêu , còn được gọi là hàm dự đoán, là kết quả của quá trình chuẩn bị hoặc đào tạo. Về mặt toán học, thách thức là tìm ra một hàm lấy một biến làm đầu vào хvà trả về giá trị dự đoán у.
Học máy dành cho nhà phát triển Java, Phần 2 - 2
Trong học máy, hàm chi phí (J(θ))được sử dụng để tính giá trị lỗi hoặc "chi phí" của hàm mục tiêu nhất định.
Học máy dành cho nhà phát triển Java, Phần 2 - 3
Hàm chi phí cho thấy mô hình phù hợp như thế nào với dữ liệu huấn luyện. Để xác định chi phí của hàm mục tiêu trình bày ở trên, cần tính sai số bình phương của từng ngôi nhà mẫu (i). Sai số là khoảng cách giữa giá trị tính toán уvà giá trị thực ycủa ngôi nhà so với ví dụ i.
Học máy dành cho nhà phát triển Java, Phần 2 - 4
Ví dụ: giá thực của một ngôi nhà có diện tích 1330 = 6.500.000 € . Và chênh lệch giữa giá nhà dự đoán theo hàm mục tiêu đã huấn luyện là €7.032.478 : chênh lệch (hoặc sai số) là €532.478 . Bạn cũng có thể thấy sự khác biệt này trong biểu đồ trên. Sự khác biệt (hoặc lỗi) được hiển thị dưới dạng các đường đứt nét dọc màu đỏ cho từng cặp đào tạo vùng giá. Sau khi tính chi phí của hàm mục tiêu đã huấn luyện, bạn cần tính tổng sai số bình phương của mỗi ngôi nhà trong ví dụ và tính giá trị chính. Giá trị càng thấp (J(θ))thì dự đoán về hàm mục tiêu của chúng ta càng chính xác. Liệt kê 3 cho thấy cách triển khai Java đơn giản của hàm chi phí lấy đầu vào là hàm mục tiêu, danh sách dữ liệu huấn luyện và các nhãn liên quan đến chúng. Các giá trị dự đoán sẽ được tính theo vòng lặp và sai số sẽ được tính bằng cách trừ đi giá trị thực (lấy từ nhãn). Sau đó, bình phương các sai số sẽ được tính tổng và giá trị sai số sẽ được tính toán. Chi phí sẽ được trả về dưới dạng giá trị của loại double:

Liệt kê-3

public static double cost(Function<ltDouble[], Double> targetFunction,
 List<ltDouble[]> dataset,
 List<ltDouble> labels) {
 int m = dataset.size();
 double sumSquaredErrors = 0;

 // рассчет квадрата ошибки («разницы») для каждого тренировочного примера и //добавление его к сумме
 for (int i = 0; i < m; i++) {
 // получаем вектор признаков из текущего примера
 Double[] featureVector = dataset.get(i);
 // предсказываем meaning и вычисляем ошибку базируясь на реальном
 //значении (метка)
 double predicted = targetFunction.apply(featureVector);
 double label = labels.get(i);
 double gap = predicted - label;
 sumSquaredErrors += Math.pow(gap, 2);
 }

 // Вычисляем и возращаем meaning ошибки (чем меньше тем лучше)
 return (1.0 / (2 * m)) * sumSquaredErrors;
}
Bạn muốn đọc về Java? Hãy tham gia nhóm Nhà phát triển Java !

Tìm hiểu chức năng mục tiêu

Mặc dù hàm chi phí giúp đánh giá chất lượng của hàm mục tiêu và tham số theta nhưng bạn vẫn cần tìm tham số theta phù hợp nhất. Bạn có thể sử dụng thuật toán giảm độ dốc cho việc này.

Xuống dốc

Độ dốc giảm thiểu hàm chi phí. Điều này có nghĩa là nó được sử dụng để tìm các tham số theta có chi phí tối thiểu (J(θ))dựa trên dữ liệu huấn luyện. Đây là thuật toán đơn giản hóa để tính toán các giá trị theta mới, phù hợp hơn:
Học máy dành cho nhà phát triển Java, Phần 2 - 5
Vì vậy, các tham số của vectơ theta sẽ được cải thiện sau mỗi lần lặp của thuật toán. Hệ số học α ​​chỉ định số lần tính toán ở mỗi lần lặp. Những tính toán này có thể được thực hiện cho đến khi tìm thấy giá trị theta "tốt". Ví dụ: hàm hồi quy tuyến tính bên dưới có ba tham số theta:
Học máy dành cho nhà phát triển Java, Phần 2 - 6
Ở mỗi lần lặp, một giá trị mới sẽ được tính cho từng tham số theta: , , và . Sau mỗi lần lặp, một cách triển khai mới phù hợp hơn có thể được tạo bằng cách sử dụng vectơ theta mới 0 , θ 1 , θ 2 } . Liệt kê -4 hiển thị mã Java cho thuật toán phân rã độ dốc. Theta cho hàm hồi quy sẽ được huấn luyện bằng cách sử dụng dữ liệu huấn luyện, dữ liệu đánh dấu, tốc độ học tập … Kết quả sẽ là một hàm mục tiêu được cải thiện bằng cách sử dụng các tham số theta. Phương thức này sẽ được gọi đi gọi lại, truyền hàm mục tiêu mới và các tham số theta mới từ các phép tính trước đó. Và các lệnh gọi này sẽ được lặp lại cho đến khi hàm mục tiêu được định cấu hình đạt đến mức ổn định tối thiểu: θ0θ1θ2LinearRegressionFunction(α)train()

Liệt kê-4

public static LinearRegressionFunction train(LinearRegressionFunction targetFunction,
 List<ltDouble[]> dataset,
 List<ltDouble> labels,
 double alpha) {
 int m = dataset.size();
 double[] thetaVector = targetFunction.getThetas();
 double[] newThetaVector = new double[thetaVector.length];

 // вычисление нового значения тета для каждого element тета массива
 for (int j = 0; j < thetaVector.length; j++) {
 // сумируем разницу ошибки * признак
 double sumErrors = 0;
 for (int i = 0; i < m; i++) {
 Double[] featureVector = dataset.get(i);
 double error = targetFunction.apply(featureVector) - labels.get(i);
 sumErrors += error * featureVector[j];
 }

 //вычисляем новые значения тета
 double gradient = (1.0 / m) * sumErrors;
 newThetaVector[j] = thetaVector[j] - alpha * gradient;
 }

 return new LinearRegressionFunction(newThetaVector);
}
Để đảm bảo chi phí liên tục giảm, bạn có thể chạy hàm chi phí J(θ)sau mỗi bước đào tạo. Sau mỗi lần lặp, chi phí sẽ giảm. Nếu điều này không xảy ra, điều đó có nghĩa là giá trị của hệ số học quá lớn và thuật toán đơn giản là đã bỏ lỡ giá trị tối thiểu. Trong trường hợp như vậy, thuật toán phân rã gradient không thành công. Các đồ thị bên dưới hiển thị hàm mục tiêu bằng cách sử dụng các tham số theta mới được tính toán, bắt đầu bằng vectơ theta bắt đầu {1.0, 1.0}. Cột bên trái hiển thị đồ thị của hàm dự đoán sau 50 lần lặp; cột giữa sau 200 lần lặp lại; và cột bên phải sau 1000 lần lặp lại. Từ những điều này, chúng ta có thể thấy rằng giá giảm sau mỗi lần lặp và hàm mục tiêu mới ngày càng phù hợp hơn. Sau 500-600 lần lặp lại, các thông số theta không còn thay đổi đáng kể và giá đạt đến mức ổn định. Sau này, độ chính xác của hàm mục tiêu không thể được cải thiện theo cách này.
Học máy dành cho nhà phát triển Java, Phần 2 - 7
Trong trường hợp này, mặc dù chi phí không còn giảm đáng kể sau 500-600 lần lặp nhưng hàm mục tiêu vẫn chưa tối ưu. Điều này cho thấy một sự khác biệt . Trong học máy, thuật ngữ "không nhất quán" được sử dụng để chỉ ra rằng thuật toán học không tìm thấy xu hướng cơ bản trong dữ liệu. Dựa trên kinh nghiệm thực tế, có thể dự đoán giá mỗi mét vuông sẽ giảm đối với những bất động sản lớn hơn. Từ đó, chúng ta có thể kết luận rằng mô hình được sử dụng cho quá trình học hàm mục tiêu không đủ phù hợp với dữ liệu. Sự khác biệt thường là do sự đơn giản hóa quá mức của mô hình. Điều này đã xảy ra trong trường hợp của chúng tôi, hàm mục tiêu quá đơn giản và để phân tích, nó sử dụng một tham số duy nhất - diện tích của ngôi nhà. Nhưng thông tin này không đủ để dự đoán chính xác giá nhà.

Thêm các tính năng và nhân rộng chúng

Nếu bạn nhận thấy hàm mục tiêu của mình không tương ứng với vấn đề bạn đang cố gắng giải quyết thì cần phải điều chỉnh nó. Một cách phổ biến để khắc phục sự không nhất quán là thêm các tính năng bổ sung vào vectơ đặc tính. Trong ví dụ về giá một ngôi nhà, bạn có thể thêm các đặc điểm như số phòng hoặc tuổi của ngôi nhà. Nghĩa là, thay vì sử dụng một vectơ có một giá trị đặc trưng {size}để mô tả một ngôi nhà, bạn có thể sử dụng một vectơ có nhiều giá trị, ví dụ: {size, number-of-rooms, age}. Trong một số trường hợp, số lượng đặc điểm trong dữ liệu huấn luyện có sẵn là không đủ. Sau đó, bạn nên thử sử dụng các tính năng đa thức được tính toán bằng các tính năng hiện có. Ví dụ: bạn có cơ hội mở rộng hàm mục tiêu để xác định giá một ngôi nhà sao cho nó bao gồm đặc điểm tính toán theo mét vuông (x2):
Học máy dành cho nhà phát triển Java, Phần 2 - 8
Việc sử dụng nhiều tính năng yêu cầu chia tỷ lệ tính năng , được sử dụng để chuẩn hóa phạm vi giữa các tính năng khác nhau. Do đó, phạm vi giá trị cho thuộc tính size 2 lớn hơn đáng kể so với phạm vi giá trị cho thuộc tính size. Nếu không chia tỷ lệ tính năng, kích thước 2 sẽ ảnh hưởng quá mức đến hàm chi phí. Lỗi do thuộc tính size 2 đưa ra sẽ lớn hơn đáng kể so với lỗi do thuộc tính size đưa ra. Một thuật toán chia tỷ lệ tính năng đơn giản được đưa ra dưới đây:
Học máy dành cho nhà phát triển Java, Phần 2 - 9
Thuật toán này được triển khai trong lớp FeaturesScalingtrong mã ví dụ bên dưới. Lớp này FeaturesScalingtrình bày một phương pháp thương mại để tạo hàm chia tỷ lệ được điều chỉnh theo dữ liệu huấn luyện. Trong nội bộ, các phiên bản dữ liệu huấn luyện được sử dụng để tính giá trị trung bình, tối thiểu và tối đa. Hàm kết quả lấy vectơ đặc trưng và tạo ra một vectơ mới với các đặc tính được chia tỷ lệ. Chia tỷ lệ tính năng là cần thiết cho cả quá trình học tập và quá trình dự đoán, như được hiển thị bên dưới:
// создание массива данных
List<ltDouble[]> dataset = new ArrayList<>();
dataset.add(new Double[] { 1.0, 90.0, 8100.0 }); // feature vector of house#1
dataset.add(new Double[] { 1.0, 101.0, 10201.0 }); // feature vector of house#2
dataset.add(new Double[] { 1.0, 103.0, 10609.0 }); // ...
//...

// создание меток
List<ltDouble> labels = new ArrayList<>();
labels.add(249.0); // price label of house#1
labels.add(338.0); // price label of house#2
labels.add(304.0); // ...
//...

// создание расширенного списка признаков
Function<ltDouble[], Double[]> scalingFunc = FeaturesScaling.createFunction(dataset);
List<ltDouble[]> scaledDataset = dataset.stream().map(scalingFunc).collect(Collectors.toList());

// создаем функцию которая инициализирует теты и осуществляет обучение //используя коэффициент обучения 0.1

LinearRegressionFunction targetFunction = new LinearRegressionFunction(new double[] { 1.0, 1.0, 1.0 });
for (int i = 0; i < 10000; i++) {
 targetFunction = Learner.train(targetFunction, scaledDataset, labels, 0.1);
}

// делаем предсказание стоимости дома с площадью 600 m2
Double[] scaledFeatureVector = scalingFunc.apply(new Double[] { 1.0, 600.0, 360000.0 });
double predictedPrice = targetFunction.apply(scaledFeatureVector);
Khi ngày càng có nhiều đặc tính được thêm vào, độ phù hợp của hàm mục tiêu sẽ tăng lên, nhưng hãy cẩn thận. Nếu bạn đi quá xa và thêm quá nhiều tính năng, cuối cùng bạn có thể học được một hàm mục tiêu quá phù hợp.

Quá khớp và xác thực chéo

Quá khớp xảy ra khi hàm mục tiêu hoặc mô hình quá khớp với dữ liệu huấn luyện, đến mức nó thu được nhiễu hoặc các biến thể ngẫu nhiên trong dữ liệu huấn luyện. Một ví dụ về trang bị quá mức được hiển thị trong biểu đồ ngoài cùng bên phải bên dưới:
Học máy dành cho nhà phát triển Java, Phần 2 - 10
Tuy nhiên, mô hình trang bị quá mức hoạt động rất tốt trên dữ liệu huấn luyện nhưng sẽ hoạt động kém trên dữ liệu thực chưa xác định. Có một số cách để tránh trang bị quá mức.
  • Sử dụng tập dữ liệu lớn hơn để đào tạo.
  • Sử dụng ít tính năng hơn như minh họa trong biểu đồ trên.
  • Sử dụng thuật toán học máy cải tiến có tính đến tính chính quy.
Nếu một thuật toán dự đoán phù hợp quá mức với dữ liệu huấn luyện thì cần phải loại bỏ các tính năng không mang lại lợi ích cho độ chính xác của nó. Khó khăn là tìm ra những đặc điểm có ảnh hưởng đáng kể đến độ chính xác của dự đoán hơn những đặc điểm khác. Như được hiển thị trong biểu đồ, mức độ phù hợp quá mức có thể được xác định một cách trực quan bằng cách sử dụng biểu đồ. Điều này hoạt động tốt đối với các biểu đồ có 2 hoặc 3 tọa độ, việc vẽ và đánh giá biểu đồ sẽ trở nên khó khăn nếu bạn sử dụng nhiều hơn 2 tính năng. Trong xác thực chéo, bạn kiểm tra lại các mô hình sau khi đào tạo bằng cách sử dụng dữ liệu mà thuật toán chưa biết sau khi quá trình đào tạo hoàn tất. Dữ liệu được dán nhãn có sẵn nên được chia thành 3 bộ:
  • dữ liệu đào tạo;
  • dữ liệu xác minh;
  • dữ liệu thử nghiệm.
Trong trường hợp này, 60% bản ghi được dán nhãn mô tả các ngôi nhà sẽ được sử dụng trong quá trình huấn luyện các biến thể của thuật toán đích. Sau quá trình huấn luyện, một nửa dữ liệu còn lại (chưa được sử dụng trước đó) sẽ được sử dụng để xác minh rằng thuật toán mục tiêu đã huấn luyện hoạt động tốt trên dữ liệu chưa biết. Thông thường, thuật toán hoạt động tốt hơn các thuật toán khác sẽ được chọn để sử dụng. Dữ liệu còn lại được sử dụng để tính giá trị lỗi cho mô hình được chọn cuối cùng. Có các kỹ thuật xác thực chéo khác, chẳng hạn như k-fold . Tuy nhiên, tôi sẽ không mô tả chúng trong bài viết này.

Công cụ học máy và khung Weka

Hầu hết các khung và thư viện đều cung cấp một bộ sưu tập lớn các thuật toán học máy. Ngoài ra, chúng còn cung cấp giao diện cấp cao thuận tiện cho việc đào tạo, thử nghiệm và xử lý các mô hình dữ liệu. Weka là một trong những framework phổ biến nhất dành cho JVM. Weka là một thư viện Java thực tế chứa các bài kiểm tra đồ họa để xác thực các mô hình. Ví dụ bên dưới sử dụng thư viện Weka để tạo tập dữ liệu huấn luyện chứa các tính năng và nhãn. Phương pháp setClassIndex()- để đánh dấu. Trong Weka, nhãn được định nghĩa là một lớp:
// определяем атрибуты для признаков и меток
ArrayList<ltAttribute> attributes = new ArrayList<>();
Attribute sizeAttribute = new Attribute("sizeFeature");
attributes.add(sizeAttribute);
Attribute squaredSizeAttribute = new Attribute("squaredSizeFeature");
attributes.add(squaredSizeAttribute);
Attribute priceAttribute = new Attribute("priceLabel");
attributes.add(priceAttribute);


// создаем и заполняем список признаков 5000 примеров
Instances trainingDataset = new Instances("trainData", attributes, 5000);
trainingDataset.setClassIndex(trainingSet.numAttributes() - 1);
Instance instance = new DenseInstance(3);

instance.setValue(sizeAttribute, 90.0);
instance.setValue(squaredSizeAttribute, Math.pow(90.0, 2));
instance.setValue(priceAttribute, 249.0);
trainingDataset.add(instance);
Instance instance = new DenseInstance(3);
instance.setValue(sizeAttribute, 101.0);
...
Tập dữ liệu và Đối tượng mẫu có thể được lưu và tải từ một tệp. Weka sử dụng ARFF (Định dạng tệp quan hệ thuộc tính) được hỗ trợ bởi điểm chuẩn đồ họa của Weka. Tập dữ liệu này được sử dụng để huấn luyện một hàm mục tiêu được gọi là bộ phân loại trong Weka. Trước hết, bạn phải xác định hàm mục tiêu. Mã bên dưới LinearRegressionsẽ tạo một phiên bản của trình phân loại. Trình phân loại này sẽ được đào tạo bằng cách sử dụng buildClassifier(). Phương pháp buildClassifier()chọn tham số theta dựa trên dữ liệu huấn luyện để tìm kiếm mô hình mục tiêu tốt nhất. Với Weka, bạn không phải lo lắng về việc thiết lập tốc độ học hoặc số lần lặp. Weka cũng thực hiện chia tỷ lệ tính năng một cách độc lập.
Classifier targetFunction = new LinearRegression();
targetFunction.buildClassifier(trainingDataset);
Khi các cài đặt này được thực hiện, hàm mục tiêu có thể được sử dụng để dự đoán giá của một ngôi nhà, như minh họa bên dưới:
Instances unlabeledInstances = new Instances("predictionset", attributes, 1);
unlabeledInstances.setClassIndex(trainingSet.numAttributes() - 1);
Instance unlabeled = new DenseInstance(3);
unlabeled.setValue(sizeAttribute, 1330.0);
unlabeled.setValue(squaredSizeAttribute, Math.pow(1330.0, 2));
unlabeledInstances.add(unlabeled);

double prediction = targetFunction.classifyInstance(unlabeledInstances.get(0));
Weka cung cấp một lớp Evaluationđể kiểm tra mô hình hoặc phân loại đã được đào tạo. Trong mã bên dưới, một mảng dữ liệu xác thực đã chọn được sử dụng để tránh kết quả sai. Kết quả đo (chi phí sai số) sẽ được hiển thị trên bảng điều khiển. Thông thường, kết quả đánh giá được sử dụng để so sánh các mô hình được đào tạo bằng các thuật toán học máy khác nhau hoặc các biến thể của các thuật toán sau:
Evaluation evaluation = new Evaluation(trainingDataset);
evaluation.evaluateModel(targetFunction, validationDataset);
System.out.println(evaluation.toSummaryString("Results", false));
Ví dụ trên sử dụng hồi quy tuyến tính, dự đoán các giá trị số, chẳng hạn như giá một ngôi nhà, dựa trên các giá trị đầu vào. Hồi quy tuyến tính hỗ trợ dự đoán các giá trị số liên tục. Để dự đoán các giá trị nhị phân (“Có” và “Không”), bạn cần sử dụng các thuật toán học máy khác. Ví dụ: cây quyết định, mạng lưới thần kinh hoặc hồi quy logistic.
// использование логистической регрессии
Classifier targetFunction = new Logistic();
targetFunction.buildClassifier(trainingSet);
Ví dụ: bạn có thể sử dụng một trong các thuật toán này để dự đoán liệu một email có phải là thư rác hay dự đoán thời tiết hay dự đoán liệu một ngôi nhà có bán chạy hay không. Nếu bạn muốn dạy thuật toán của mình dự đoán thời tiết hoặc tốc độ bán một ngôi nhà, bạn cần một tập dữ liệu khác, ví dụ:topseller:
// использование атрибута маркера topseller instead of атрибута маркера цена
ArrayList<string> classVal = new ArrayList<>();
classVal.add("true");
classVal.add("false");

Attribute topsellerAttribute = new Attribute("topsellerLabel", classVal);
attributes.add(topsellerAttribute);
Tập dữ liệu này sẽ được sử dụng để huấn luyện một bộ phân loại mới topseller. Sau khi được huấn luyện, lệnh gọi dự đoán sẽ trả về chỉ mục lớp mã thông báo có thể được sử dụng để nhận giá trị dự đoán.
int idx = (int) targetFunction.classifyInstance(unlabeledInstances.get(0));
String prediction = classVal.get(idx);

Phần kết luận

Mặc dù học máy có liên quan chặt chẽ đến thống kê và sử dụng nhiều khái niệm toán học, bộ công cụ học máy cho phép bạn bắt đầu tích hợp học máy vào các chương trình của mình mà không cần kiến ​​thức sâu về toán học. Tuy nhiên, càng hiểu rõ các thuật toán học máy cơ bản, chẳng hạn như thuật toán hồi quy tuyến tính mà chúng tôi đã khám phá trong bài viết này, bạn càng có thể chọn thuật toán phù hợp và điều chỉnh nó để có hiệu suất tối ưu. Dịch từ tiếng Anh. Tác giả: Gregor Roth, Kiến trúc sư phần mềm, JavaWorld.
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION