KHÁI QUÁT VỀ NGÔN NGỮ C#
Khái niệm NET
The NET Framework is a class library that can be utilized with any NET language to perform tasks ranging from string manipulation to generating dynamic web pages (ASP.NET), XML parsing, and reflection It is organized into a collection of namespaces that group related classes together, such as System.Drawing for graphics, System.Collections for data structures, and System.Windows.Forms for Windows Forms applications.
.NET là một môi trường phát triển mạnh mẽ cho các ngôn ngữ lập trình, cung cấp khả năng quản lý bộ nhớ và tài nguyên hiệu quả Hai thành phần chính của NET bao gồm CLR (Common Language Runtime) và NET Framework Class Library, tạo nền tảng vững chắc cho việc xây dựng ứng dụng.
Hoạt động của NET
chương t nh sẽ được biên dịch thành MSIL (Microsoft Intermediate Language)
Dịch IL( Intermediate Language ) thành nền cụ thể của NET bằng CLR(Common Language Runtime)
.NET hỗ trợ nhiều ngôn ngữ lập trình như C++, VB.NET, Managed C++, J+ và J#, cho phép biên dịch và sử dụng các thành phần cũng như thuộc tính từ ngôn ngữ khác Điều này thể hiện khả năng linh hoạt của C#, nhờ vào Intermediate Language (IL) với các thuộc tính đặc biệt.
1 Hỗ trợ hướng đối tượng và giao diện ( interface )
2 Phân biệt giữa kiểu giá trị và kiểu tham chiếu
4 Quản lỗi thông qua các ngoại lệ
5 Sử dụng các thuộc tính
1.2.1 Hỗ trợ hướng đối tượng và sử dụng giao diện
Công nghệ IL mang lại nhiều lợi ích cho các ngôn ngữ lập trình hướng đối tượng, và không phải ngẫu nhiên mà các thư viện cơ sở của NET được thiết kế với giao diện thân thiện.
COM là một chuẩn nhị phân cho phép các thành phần tương tác mà không cần quan tâm đến ngôn ngữ lập trình, đảm bảo tính thống nhất và tương thích Tuy nhiên, do không hỗ trợ tính thừa kế, COM mất đi một số lợi ích của lập trình hướng đối tượng.
Với khả năng hỗ trợ nhiều ngôn ngữ đồng thời, sau khi được chuyển đổi thành IL, các ngôn ngữ khác nhau có thể tương tác và làm việc hiệu quả cùng nhau.
Cụ thể là một lớp đƣợc tạo ra trong một ngôn ngữ có thể thừa kế từ một lớp đƣợc viết trong một ngôn ngữ khác
Một lớp có thể chứa thể hiện của một lớp khác không quan tâm đến ngôn ngữ đă tạo ra hai lớp đó
Một đối tượng có thể gọi trực tiếp phương thức của một đối tượng khác đƣợc viết bởi một ngôn ngữ khác
Các đối tƣợng (hoặc các tham chiếu đến các đối tƣợng) có thể đƣợc truyền qua lại giữa các hàm
Bạn có khả năng bẫy lỗi từng bước chương t nh nguồn giữa các ngôn khác nhau
1.2.2 Phân biệt kiểu giá trị và kiểu tham chiếu
Trong IL, có sự phân biệt rõ ràng giữa kiểu giá trị và kiểu tham chiếu Các kiểu giá trị được lưu trữ trong vùng Stack, trong khi các kiểu tham chiếu được lưu trong vùng Heap.
1.2.3 Định kiểu mạnh ràng đối với từng kiểu dữ liệu trả về, các kiểu dữ liệu luôn đƣợc đánh dấu cụ thể Điều này là hoàn toàn phù hợp với đặc tính hỗ trợ nền cho nhiều loại ngôn ngữ của NET Một vấn đề nảy sinh đó là có những kiểu đƣợc hỗ trợ trong ngôn ngữ này nhƣng lại không đƣợc hỗ trợ trong ngôn ngữ khác hoặc là nếu một lớp xuất nó cần phải biết tất cả các kiểu dùng trong các lớp đó
CTS: Để đáp ứng đƣợc tác vụ đó, IL sử dụng tiến trình CTS – Common
Hệ thống kiểu (Type System) trong NET là một bộ phận quan trọng, đảm bảo rằng tất cả các kiểu dữ liệu từ các ngôn ngữ lập trình khác nhau đều được biên dịch thành một kiểu chung, giúp tạo ra sự đồng nhất và tương thích trong môi trường NET.
CLS phối hợp với CTS nhằm đảm bảo sự tương thích giữa các ngôn ngữ lập trình Đây là một chuẩn mà tất cả các ngôn ngữ hỗ trợ NET cần tuân thủ CLS hoạt động dựa trên hai nguyên tắc chính.
CLS không hoàn n bó buộc các ngôn ngữ lập trình, điều này khiến cho các ngôn ngữ hoàn toàn có thể phát triển theo các chiều hướng riêng
CLS gắn một chuẩn lên các ngôn ngữ lập t nh biết NET, điều này đảm bảo m của các ngôn ngữ đó luôn đƣợc hỗ trợ khi biên dịch
Garbage collector (GC) là thành phần quản lý bộ nhớ trong NET, đóng vai trò quan trọng trong hiệu suất của C# GC hoạt động nhằm giải phóng bộ nhớ không còn sử dụng, giúp tối ưu hóa hiệu suất ứng dụng trên nền tảng NET.
Sau khi được biên dịch, các m sẽ được đưa hoàn toàn vào Heap Khi Heap đầy, Garbage Collector (GC) sẽ so sánh với các m đang thực hiện Nếu phát hiện các kết quả không còn sử dụng, GC sẽ tiến hành dọn dẹp và giải phóng bộ nhớ.
1.2.4 Bắt lỗi xử dụng các ngoại lệ
.NET được phát triển nhằm đơn giản hóa quá trình xử lý lỗi thông qua việc sử dụng các ngoại lệ Ý tưởng chính là tạo ra một khu vực được thiết kế đặc biệt để quản lý các ngoại lệ, giúp lập trình viên dễ dàng kiểm soát và xử lý các tình huống lỗi trong ứng dụng của mình.
Cấu trúc ngoại lệ trong lập trình cho phép xác định các điều kiện có thể gây ra lỗi, từ đó chuyển hướng luồng thực thi đến thủ tục quản lý ngoại lệ khi lỗi xảy ra Điều này giúp xử lý các tình huống không mong muốn một cách hiệu quả, đảm bảo chương trình hoạt động ổn định.
1.2.5 Sử dụng các thuộc tính
Các thuộc tính trong IL cho phép người dùng có thể sử dụng dễ dàng hoặc có thể tự thiết lập các thuộc tính của riêng họ
Tiến trình biên dịch thành m NET – Common language runtime
CLR có nh IL thành m nh biên dịch kiểu just in time ( JIT ), khác với kiểu thông dịch trong Java
Thay vì biên dịch toàn bộ mã nguồn, JIT chỉ biên dịch từng phần khi cần thiết Kết quả biên dịch sẽ được lưu trữ trong bộ nhớ cho đến khi ứng dụng kết thúc, giúp máy tính không phải biên dịch lại trong các lần xử lý tiếp theo.
.NET luôn cải thiện tốc độ hoạt động qua từng lần chạy, đảm bảo hiệu suất tối ưu Bên cạnh đó, NET cung cấp hỗ trợ tối ưu dựa trên loại vi xử lý, giúp các tiến trình hoạt động hiệu quả hơn với cách thức phù hợp.
Khái quát về C#
C# là một ngôn ngữ lập trình hướng đối tượng được phát triển bởi
Microsoft đã khởi đầu kế hoạch NET với ngôn ngữ lập trình C#, được gọi theo tên ECMA là C# Ngôn ngữ này được phát triển dựa trên C++ và Java, mang lại sự cân bằng giữa các ngôn ngữ như C++, Visual Basic, Delphi và Java.
C# đƣợc thiết kế chủ yếu bởi Anders Hejlsberg kiến trúc sƣ phần mềm nổi tiếng với các sản phẩm Turbo Pascal, Delphi, J++, WFC
C# là ngôn ngữ lập trình phản ánh sâu sắc NET Framework, nơi mà tất cả các chương trình NET được thực thi Ngôn ngữ này phụ thuộc mạnh mẽ vào Framework, với mọi dữ liệu cơ sở được xem như đối tượng và được quản lý bởi trình dọn rác Garbage-Collector (GC) Ngoài ra, các kiểu trừu tượng như class, delegate, interface và exception cũng thể hiện rõ những đặc điểm của NET runtime.
So sánh với C và C++, ngôn ngữ C# bị giới hạn và đƣợc nâng cao ở một vài đặc điểm nào đó, nhƣng không bao gồm các giới hạn sau đây:
Các con trỏ chỉ có thể được sử dụng trong chế độ không an toàn, trong khi hầu hết các đối tượng đều được tham chiếu an toàn và các phép tính được kiểm tra tràn bộ đệm Chúng chỉ được dùng để gọi các loại kiểu giá trị, trong khi các đối tượng thuộc bộ thu rác chỉ có thể được gọi thông qua tham chiếu.
Các đối tượng không thể được giải phóng tường minh
Chỉ có thể kế thừa một lớp, nhưng có khả năng cài đặt nhiều interface trừu tượng Tính năng này giúp đơn giản hóa quá trình thực thi trong thời gian thực.
C# thì an-toàn-kiểu (typesafe) hơn C++
Cú pháp khai báo mảng khác nhau("int[] a = new int[5]" thay vì "int a[5]")
Kiểu thứ tự đƣợc thay thế bằng tên miền không gian (namespace)
Có thêm Properties, các phương pháp có thể gọi các Properties để truy cập dữ liệu
Trong C# 3.0, sẽ có vài bổ sung cơ bản sau:
Các từ khóa "select, from, where" cho phép truy vấn từ một tập, từ SQL, v.v (hay còn đƣợc gọi là LINQ - viết tắt của Language INtergrated Query)
Khởi tạo đối tƣợng: Customer c = new Customer(); c.Name="James"; trở thành Customer c = new Customer { Name="James" };
Các biểu thức lambda: listOfFoo.Where(delegate(Foo x) { return x.size>10;}) trở thành listOfFoo.Where(x => x.size>10); var x = "hello"; có thể hoán đổi với string x = "hello";
Các phương thức mở rộng
C# sử dụng hệ thống kiểu/đối tượng trong NET, cho phép các chương trình C# giao tiếp dễ dàng với nhiều ngôn ngữ khác mà không gặp vấn đề về kiểu Chẳng hạn, kiểu int là bí danh của System.Int32, kế thừa từ System.Object, cho thấy rằng các kiểu primitive trong C# tương tự như các đối tượng khác Điều này được minh chứng qua việc gọi các phương thức như toString hoặc GetType trên bất kỳ kiểu primitive nào.
Trong C#, các kiểu đơn giản (simple types) mặc dù là đối tượng, nhưng được truyền theo tham trị (pass-by-value) giống như trong Java Tuy nhiên, điều đặc biệt là tất cả các kiểu đơn giản trong C# đều là các cấu trúc (struct), và khi được truyền theo tham trị, chúng sẽ được truyền theo tham biến một lần nữa.
Trong C#, có hai cách để xác định một biến hằng Đầu tiên, sử dụng từ khóa "const" để đánh dấu biến, khiến giá trị được chuyển đổi trước khi biên dịch Ví dụ, với định nghĩa "const int two = 2;", biểu thức "2 * two" sẽ được chuyển thành "2 * 2" trước khi biên dịch, giúp tăng tốc độ thực thi bằng cách loại bỏ việc tìm kiếm giá trị hằng trong quá trình chạy.
1.2.4 Cấu trúc điều kiện (Conditionals structure)
Trong C#, cấu trúc điều khiển như "if-then-else" và "switch" được sử dụng để xử lý các điều kiện khác nhau Tuy nhiên, trong cấu trúc "switch", dòng điều khiển không được phép rơi vào các trường hợp khác nhau của biểu thức switch một cách tùy ý.
Ngoài những dòng lặp: while, do-while, for C# còn có foreach
Ví dụ: foreach( int member in array )
Giao tiếp IEnumerable cho phép nhận một sự thay thế IEnumerator cho đối tượng, giúp việc lặp qua các phần tử trở nên dễ dàng Bất kỳ đối tượng nào triển khai các giao diện IEnumerable và IEnumerator đều có thể được sử dụng trong vòng lặp foreach.
1.2.6 Các phát biểu nhảy (Jumps)
Trong C#, các phát biểu điều khiển như continue, break, goto và return được sử dụng để quản lý luồng thực thi của chương trình Các phát biểu này cho phép thoát khỏi các vòng lặp hoặc chuyển hướng dòng điều khiển đến một khối lệnh khác, giúp tối ưu hóa và kiểm soát quá trình thực thi mã.
Trong C# đều là các ngoại lệ run-time, trình biên dịch sẽ không giúp đỡ các lập trình viên giữ lại trạng thái của các ngoại lệ
Có sẵn một cách đi tắt nếu đối tƣợng ngoại lệ không cần thiết bằng việc sử dụng framework dưới đây
// những lệnh có thể gây ra ngoại lệ
// xử lý những ngoại lệ mà không cần nhận một bộ xử lý cho đối tượng ngoại lệ }
Nhƣng nếu bạn cần bắt một ngoại lệ cụ thể (trong khi không yêu cầu đối tượng ngoại lệ), có thể dùng tương tự như sau: try {
1.2.7 Các phương thức (methods) public void methodCaller ( params int[] a );
Và phương thức có thể được gọi bất kỳ methodCaller ( 1 ); method Caller ( 1, 2, 3, 4 );
Bên trong phương thức, các tham số có thể được truy cập thông qua dãy
“a” đã đƣợc định nghĩa Máy tính có thể hiểu, kết quả đƣợc tìm thấy
Các thuộc tính trong C# thường được sử dụng với mô hình getter/setter tương tự như trong nhiều lớp của Java Ví dụ, một thuộc tính có thể được khai báo với biến riêng tư như sau: private int property; và phương thức công khai để truy cập giá trị là public int Property() { get { return this.property; } }.
// value là một biến được tạo ra bởi trình biên dịch để thay thế các tham số this.property = value;
Có thể dễ dàng sử dụng bên trong một chương trình C# int currentValue = Property;
1.2.9 Từ chỉ định truy cập (Accessbility Modifiers)
Access modifier bao gồm: Public, protected, internal, protected internal, private
Các modifier trong Java cho phép bạn điều chỉnh khả năng truy cập đến đối tượng, phương thức và biến Chúng ta sẽ khám phá các modifier này ngay sau đây, và sẽ bàn về đối tượng và kế thừa trong phần tiếp theo.
1.2.10 Các đối tƣợng, các lớp và các cấu trúc
Kế thừa các lớp ta dùng “:”
Ví dụ: class B : A {} nghĩa là lớp B kế thừa từ lớp A
_Struct đƣợc truyền theo tham trị thay vì theo tham biến
_Struct không thể kế thừa, tuy nhiên chúng có thể bổ sung các giao tiếp
_Struct không thể đƣợc định nghĩa một khởi dựng (contructor) mà không có tham số
Cấu trúc định nghĩa các constructor với các tham số cần thiết để xác định chính xác tất cả các trường, vì nó sẽ trả về điều khiển cho phương thức gọi nó.
C# cho phép người dùng định nghĩa chuyển đổi kiểu tự tạo cho hai đối tượng bất kỳ, bao gồm hai loại chuyển đổi: chuyển đổi tương đối và chuyển đổi tuyệt đối.
1.2.12 Tải chồng toán tử (Operator overloading)
Overloading operators in C# is straightforward The FlooredDouble class can be inherited to include a public static method that allows for the addition of two FlooredDouble instances The method is defined as follows: public static FloorDouble operator + (FloorDouble fd1, FloorDouble fd2) { return new FloorDouble(fd1.Value + fd2.Value); }.
Và các phát biểu sau là đúng
1.2.13 Tổ chức lại mã nguồn
C# không đặt bất kỳ yêu cầu nào trong việc tổ chức file – một lập trình viên có thể sắp xếp toàn bộ chương trình C# bên trong một file cs
*Giống nhƣ class chỉ gồm toàn các hàm trừu tƣợng
*Khi một class thiết đặt(implement) một giao diện thì phải thi công tất cả các hàm giao diện này
+Thiết đặt một giao diện
+Truy xuất các hàm giao diện
+Override một thiết đặt giao diện
+Dùng giao diện nhƣ thông số
+Thiết đặt kế thừa giao diện.
NHẬN DẠNG CHỮ VIẾT TAY SỬ DỤNG HẠT NHÂN PHÂN TÍCH BIỆT THỨC
Phân tích biệt thức tuyến tính
Phân tích biệt thức tuyến tính (LDA) là phương pháp thống kê và máy học nhằm tìm kiếm sự kết hợp tuyến tính của các đặc trưng để phân tách hai hoặc nhiều lớp đối tượng Kết quả từ LDA có thể được sử dụng như một bộ phân loại tuyến tính hoặc thường được áp dụng để giảm đa chiều trước khi thực hiện phân loại cuối cùng.
Phân tích biệt thức tuyến tính (LDA) có mối liên hệ chặt chẽ với phân tích thành phần chủ yếu (PCA), khi cả hai đều tìm kiếm sự kết hợp tuyến tính tối ưu của các biến mô tả dữ liệu Trong khi PCA tập trung vào việc giảm chiều dữ liệu, LDA lại hướng tới việc tối đa hóa sự khác biệt giữa các lớp dữ liệu để cải thiện độ chính xác của mô hình.
Ma trận tán xạ lớp giữa (Between-Class Scatter Matrix) và ma trận phân tán lớp trong (Within-Class Scatter Matrix) là hai khái niệm quan trọng trong phân tích dữ liệu Giải pháp tối ưu cho bài toán này có thể được xác định thông qua giá trị riêng (Eigen values) của ma trận SB.
-1SW và lấy Véctơ riêng ứng với giá riêng lớn nhất để định ra cơ sở mới cho dữ liệu.
Nhân Phân tích biệt thức
KDA là một phương pháp mở rộng của LDA, nhằm tạo ra sự phân tán phi tuyến tính tương tự như KPCA với PCA Mục tiêu chính của KDA là tối đa hóa phương sai giữa các lớp và tối thiểu hóa phương sai trong cùng một lớp Nhờ vào việc sử dụng nhân, công thức cuối cùng của KDA được biểu diễn một cách rõ ràng.
Với K c là ma trận nhân cho lớp c; u c là cột biểu diễn vecto K c,
I là ma trận nhận dạng; l c là số mẫu trong lớp c ;
1 lc là ma trận (l c xl c) với tất cả phần tử 1 / l c (nghĩa là I - 1 / l c là trung ma trận của kích thước I c )
Chức năng nhân trick và nhân chuẩn
Nhân nhị thức là một công cụ mạnh mẽ, cung cấp cầu nối giữa các thuật toán tuyến tính và phi tuyến tính thông qua giao điểm giữa hai vectơ Bằng cách xây dựng bản đồ dữ liệu đầu vào vào không gian chiều vô hạn, thuật toán tuyến tính hoạt động trong không gian này có thể xử lý các mối quan hệ phi tuyến tính trong không gian ban đầu.
Hiện nay, các nhân trick trở nên rất hữu ích vì chúng giúp lập bản đồ mà không cần tính toán phức tạp Nếu thuật toán của chúng ta chỉ có thể được biểu diễn trong miền giữa hai vectơ, thì việc thay thế tích trong miền này bằng tích trong một không gian thích hợp khác là đủ Đây chính là "trick": bất cứ khi nào một giao điểm được sử dụng, nó sẽ được thay thế.
Hàm nhân cho phép áp dụng thuật toán vào không gian nhiều chiều mà không cần lập bản đồ rõ ràng cho các điểm đầu vào Một số hàm nhân phổ biến bao gồm hàm nhân tuyến tính, hàm nhân đa thức và hàm nhân Gaussian Dưới đây là danh sách các hàm nhân với những đặc điểm thú vị nhất.
Nhân tuyến tính là hàm nhân đơn giản nhất Nó đƣợc đƣa ra bởi các tích trong
cộng với một hằng số c tùy chọn
Thuật toán nhân sử dụng một nhân tuyến tính thường tương đương với các nhân phi tuyến, tức là KPCA với nhân tuyến tính tương đương chuẩn PCA
Nhân đa thức là một nhân không tĩnh
Nó rất thích hợp cho các vấn đề tất cả dữ liệu là bình thường
Nhân Gaussian là một trong những loại nhân linh hoạt nhất và thường được ưa chuộng trong việc xây dựng mô hình khi thông tin về cấu trúc dữ liệu còn hạn chế Đây là một hàm nhân cơ sở dạng tia, giúp cải thiện hiệu quả của các thuật toán học máy.
Mô hình các lớp đƣợc sử dụng trong KDA
Các mã nguồn có trong bài này chỉ chứa các tập con của mảng cần thiết cho KDA Các lớp cũng được hiển thị trong hình dưới đây
Các lớp KDA được phát triển dựa trên Phân tích biệt thức tuyến tính, với việc mở rộng thuật toán cốt lõi thông qua việc sử dụng nhân Hệ thống cung cấp hơn 20 loại nhân khác nhau, trong đó nhân Gaussian thường là lựa chọn tối ưu cho hầu hết các ứng dụng.
2.5.1 Dữ liệu số của UCI
Kho dữ liệu Máy Học UCI (UCI Machine Learning Repository) là một tập hợp các cơ sở dữ liệu và lý thuyết miền, phục vụ cho cộng đồng nghiên cứu máy học trong việc phân tích thực nghiệm các thuật toán học máy Trong số các kho dữ liệu này, Nhận dạng chữ số viết tay là một trong những bộ dữ liệu nổi bật.
Trong dữ liệu chữ số, các chữ số được biểu diễn dưới dạng ma trận 32x32 và có thể được tiền xử lý thành các khối 4x4 không chồng chéo Việc giảm chiều dữ liệu là cần thiết để tránh vấn đề "Curse of Dimensionality" khi sử dụng phân lớp Phương pháp này giúp xử lý hiệu quả các vấn đề liên quan đến dữ liệu đa chiều lớn mà không bị giới hạn.
Mẫu chữ số được lấy ra từ kho số liệu chữ số thô
Phương pháp nhân được ưa chuộng vì khả năng áp dụng trực tiếp cho các vấn đề cần phân tích kỹ lưỡng dựa trên dữ liệu đã qua xử lý và hiểu biết sâu rộng về cấu trúc dữ liệu Ngay cả khi kiến thức về dữ liệu còn hạn chế, phương pháp nhân mù vẫn thường cho kết quả chính xác Mặc dù có thể đạt được tối ưu hóa thông qua các phương pháp nhân, việc này rất khó khăn do sự đa dạng vô hạn của các hàm nhân và không gian tinh chỉnh thông số rộng lớn cho mỗi hàm.
Các mã nguồn dưới đây minh họa một ví dụ cụ thể về KDA, đồng thời lưu ý cách xử lý đầu vào là các véc tơ đầy đủ cho 1024 vị trí Việc này sẽ không thực tế khi áp dụng Neural Networks.
/ / Giải nén đầu vào và đầu ra int samples = 500 ; double [,] input = new double [samples, 1024 ]; int [] output = new int [samples];
/ / Tạo các lựa chọn nhân với các thông số đã cho
IKernel kernel = new Gaussian(( double )numSigma.Value);
/ / Tạo các nhân phân tích biệt thức bằng cách sử dụng các nhân được lựa chọn kda = new KernelDiscriminantAnalysis(input, output, kernel);
/ / Thiết lập các ngưỡng tỷ lệ tối thiểu để giữ cho các thành phần trong phân tích kda.Threshold = ( double )numThreshold.Value;
/ / Thiết lập các qui chuẩn so sánh để tránh giải pháp đơn lẻ kda.Regularization = ( double )numRegularization.Value;
/ / Tính toán phân tích kda.Compute();
/ / Hiển thị thông tin về các dạng phân tích
/ / (Hầu hết các thuộc tính có thể đƣợc databound trực để điều khiển trực quan) dgvPrincipalComponents.DataSource = kda.Discriminants; dgvFeatureVectors.DataSource = new
ArrayDataView(kda.DiscriminantMatrix); dgvClasses.DataSource = kda.Classes;
2.5.2 Phân lớp các chữ số số bằng KDA
Nhân phân tích biệt thức vẫn giữ nguyên chất lượng sau khi áp dụng đa chiều, cho phép áp dụng trực tiếp cho các tập dữ liệu số nguyên 32x32 mà không cần tiền xử lý Chỉ cần thực hiện chuyển vị ma trận 32x32 thành vector độ dài 1024 là đủ.
KDA không phải là phương pháp hiệu quả cho tập dữ liệu lớn, với độ phức tạp O(n³) khiến thời gian phân tích không được cải thiện đáng kể khi kích thước dữ liệu tăng Hơn nữa, số lượng giải pháp của KDA cũng hạn chế hơn so với các phương pháp như Hỗ trợ Vector Machines (SVMs), dẫn đến việc tiêu tốn một lượng lớn bộ nhớ để lưu trữ các ma trận nhân đầy đủ trong quá trình phân loại.
Phân lớp sử dụng KDA thường dựa vào khoảng cách tối thiểu giữa các điểm dữ liệu dự kiến và không gian đặc trưng.
Vấn đề phân lớp Yin Yang được minh họa qua hình ảnh bên trái, thể hiện không gian đầu vào ban đầu Hình ảnh bên phải cho thấy kết quả của một nhân phân tích biệt thức, được biểu diễn bởi một nhân Gaussian với sigma được thiết lập là 1,0.
Các dấu chấm màu đỏ trong hình ảnh bên phải thể hiện phép chiếu của dấu chấm màu đỏ trong hình ảnh bên trái, với sự chú ý rằng chúng gần với lớp màu xanh trong cả hai hình ảnh Trong phân tích không gian đặc trưng, các lớp đã được trải ra thành nhiều tuyến tính Trong trường hợp này, khoảng cách Euclide (hay còn gọi là khoảng cách Malahanobis) đến lớp giá trị trung bình trong không gian đặc trưng sẽ trở thành các lớp gần nhau.
Các mã sau đây chứng minh việc phân lớp của các trường hợp mới sử dụng một nhân đã đƣợc tính toán phân tích biệt thức
// Lấy các vector đầu vào double [] input = canvas.GetDigit();
// Phân loại các vector đầu vào int num = kda.Classify(input);
// Thiết lập câu trả lời phân loại thực tế lbClassification.Text = num.ToString();
CHƯƠNG TRÌNH THỬ NGHIỆM
Kiểm tra ứng dụng
Thử nghiệm mã nguồn nhận dạng chữ số viết tay bằng KDA sẽ được thực hiện bằng cách chạy ứng dụng, và nên chạy chương trình mà không cần thông qua Visual Studio.
2008 Nhắp chuột vào menu Tệp và chọn Mở Thao tác sẽ tải một số mục từ kho dữ liệu chữ số vào ứng dụng
Dữ liệu chữ số đƣợc tải vào ứng dụng
Bên phải là thông tin chi tiết về từng lớp của các chữ số viết tay Để tiến hành phân tích, hãy nhấp vào nút PHÂN TÍCH Kết quả phân tích sẽ được thể hiện qua một đồ thị hình tròn, giúp việc kiểm tra trở nên dễ dàng hơn.
Phân tích biệt thức là một công cụ quan trọng trong việc đánh giá các yếu tố ảnh hưởng đến không gian đầu vào và kích thước ban đầu Những yếu tố này không chỉ giúp tối ưu hóa quy trình mà còn nâng cao hiệu quả trong việc xử lý dữ liệu Việc hiểu rõ mối liên quan giữa các yếu tố này sẽ mang lại lợi ích lớn cho việc ra quyết định và phát triển chiến lược.
1024, chỉ có 9 là quan trong để đƣợc chọn cho việc phân lớp
Sau khi hoàn thành phân tích, chúng ta tiến hành kiểm tra khả năng phân lớp của mô hình trong kho dữ liệu Các hàng cây xanh được xác định chính xác nhờ khoảng cách biệt thức phân loại theo không gian Euclide Kết quả cho thấy mô hình nhận diện chính xác 92% dữ liệu thử nghiệm, trong khi dữ liệu đào tạo được phân chia và xử lý độc lập.
Sử dụng các giá trị mặc định trong ứng dụng đạt đƣợc độ chính xác 92%
Sau khi hoàn tất và xác nhận phân tích, chúng ta có thể áp dụng công thức biệt thức Mỗi lớp sẽ có một công thức riêng, giúp xác định kết quả đầu ra gần nhất cho từng điểm đầu vào Quá trình phân loại này dựa trên việc tối đa hóa giá trị đầu ra.
Mã lệnh trong chương trình viết cho một số các class
3.2.1 Class Linear Discrimnant Analysis using System; using System.Collections.ObjectModel; using System.Linq; using Accord.Math; using Accord.Math.Decompositions; namespace Accord.Statistics.Analysis
The article discusses key variables used in a statistical analysis framework, including dimensions, sample sizes, and class counts It highlights essential arrays such as total means, standard deviations, and class-specific metrics, which are crucial for understanding data distribution Additionally, it mentions the importance of scatter matrices (Sw, Sb, St) and eigenvalues in the analysis process The structure also includes transformed class means and discriminant proportions, which are vital for interpreting results and making informed decisions based on the data.
# Phần kiến tạo public LinearDiscriminantAnalysis(double[,] inputs, int[] output)
// Lấy số từ các class int startingClass = output.Min(); this.classes = output.Max() - startingClass + 1;
// Lưu trữ các dữ liệu gốc this.source = inputs; this.outputs = output; this.samples = inputs.GetLength(0); this.dimension = inputs.GetLength(1);
To create simple structures for storing information, initialize arrays such as this.classCount as an integer array for classes, this.classMeans as a two-dimensional double array for class means, this.classStdDevs as a two-dimensional double array for class standard deviations, and this.classScatter as a two-dimensional double array for class scatter values.
// Tạo cấu trúc hướng đối tượng để giữ các thong tin về class
DiscriminantAnalysisClass[classes]; for (int i = 0; i < classes; i++) this.classCollection = new
/// Quay lại nguồn dữ lieu cung cấp ban đầu để phân tích public double[,] Source
/// Lấy giá trị trung bình của dữ liệu nguồn để đưa phướng án public double[] Means
{ get { return totalMeans; } protected set { totalMeans = value; }
/// Lấy trung bình tiêu chuẩn của các dữ liệu ban đầu cho phương pháp public double[] StandardDeviations
/// sử dụng ma trận lớp trong cho dữ liệu public double[,] ScatterWithinClass
{ get { return Sw; } protected set { Sw = value; }
/// sử dụng ma trận lớp giữa cho dữ liệu public double[,] ScatterBetweenClass
{ get { return Sb; } protected set { Sb = value; }
/// Lấy tổng ma trận lớp cho dữ liệu public double[,] ScatterMatrix
{ get { return St; } protected set { St = value; }
{ get { return eigenVectors; } protected set { eigenVectors = value; }
/// Sử dụng các lớp trong mỗi biệt thức
/// trong không gian biệt thức public double[] Proportions
# Phần khai báo phương thức public virtual void Compute()
// Tính toán toàn bộ dữ liệu thiết lập các biện pháp
StandardDeviations = Tools.StandardDeviation(source, totalMeans); double total = dimension;
// Khởi tạo các ma trận tán xạ this.Sw = new double[dimension, dimension]; this.Sb = new double[dimension, dimension];
// Cho mỗi lớp for (int c = 0; c < Classes.Count; c++)
// Lấy các lớp con… double[,] subset = Classes[c].Subset; int count = subset.GetLength(0);
// Tiếp tục xây dựng Ma trận tán xạ lớp trong double[,] Swi = Tools.Scatter(subset, mean, (double)count);
// Sw = Sw + Swi for (int i = 0; i < dimension; i++) for (int j = 0; j < dimension; j++)
// Tiếp tục xây dựng Ma trận tán xạ lớp giữa double[] d = mean.Subtract(totalMeans); double[,] Sbi = d.Multiply(d.Transpose()).Multiply(total);
// Sb = Sb + Sbi for (int i = 0; i < dimension; i++) for (int j = 0; j < dimension; j++)
// Lưu các thông tin được thêm this.classScatter[c] = Swi; this.classCount[c] = count; this.classMeans[c] = mean; this.classStdDevs[c] = Tools.StandardDeviation(subset, mean);
// Tính tóan sự phân ly của giá trị riêng
EigenvalueDecomposition evd = new EigenvalueDecomposition(Matrix.Inverse(Sw).Multiply(Sb));
// Lấy các giá trị riêng, tương ứng với các vecto riêng double[] evals = evd.RealEigenvalues; double[,] eigs = evd.Eigenvectors; eigs = Matrix.Sort(evals, eigs, new GeneralComparer(ComparerDirection.Descending, true));
// Lưu trữ thông tin this.Eigenvalues = evals; this.DiscriminantMatrix = eigs;
// Tạo hàm biệt thức độ xiên bias = new double[classes]; for (int i = 0; i < classes; i++)
{ bias[i] = (-0.5).Multiply(classMeans[i]).Multiply( eigs.Multiply(classMeans[i])) +
System.Math.Log(classCount[i] / total);
// Taọ các phép chiếu this.result = new double[dimension, dimension]; for (int i = 0; i < dimension; i++) for (int j = 0; j < dimension; j++) for (int k = 0; k < dimension; k++) result[i, j] += source[i, k] * eigenVectors[k, j]; createDiscriminants();
} public double[,] Transform(double[,] data)
{ return Transform(data, discriminantCollection.Count);
} public virtual double[,] Transform(double[,] data, int discriminants)
{ int rows = data.GetLength(0); int cols = data.GetLength(1);
// Cộng dữ liệu ma trận bằng cách chọn các véctơ riêng for (int i = 0; i < rows; i++) for (int j = 0; j < discriminants; j++) for (int k = 0; k < cols; k++) r[i, j] += data[i, k] * eigenVectors[k, j]; return r;
} public double[] Transform(double[] data)
{ return Transform(data.ToMatrix()).GetRow(0);
} public double[] Transform(double[] data, int discriminants)
{ return Transform(data.ToMatrix(),discriminants).GetRow(0);
} public int GetNumberOfDimensions(float threshold)
{ if (threshold < 0 || threshold > 1.0) throw new ArgumentException("Threshold should be a value between
0 and 1", "threshold"); for (int i = 0; i < discriminantCumulative.Length; i++)
} public int Classify(double[] input)
// Chọn lớp với hàm biệt thức cao hơn int imax = 0; double max = discriminantFunction(0, projection); for (int i = 1; i < classCollection.Count; i++)
{ double fy = discriminantFunction(i, projection); if (fy > max)
} public int Classify(double[] input, out double[] responses)
{ double[] projection = Transform(input); responses = new double[classCollection.Count]; int imax = 0; double max = discriminantFunction(0, projection); responses[0] = max; for (int i = 1; i < classCollection.Count; i++)
{ double fy = discriminantFunction(i, projection); responses[i] = fy; if (fy > max)
{ max = fy; imax = i; return classCollection[imax].Number;
} public int[] Classify(double[][] inputs)
{ int[] output = new int[inputs.Length]; for (int i = 0; i < inputs.Length; i++) output[i] = Classify(inputs[i]); return output;
} internal virtual double discriminantFunction(int c, double[] projection)
{ return classMeans[c].Multiply(projection) + bias[c];
# Phần bảo vệ phương thức protected void createDiscriminants()
{ int numDiscriminants = eigenValues.Length; discriminantProportions = new double[numDiscriminants]; discriminantCumulative = new double[numDiscriminants];
// Tính toán tổng ma trận phân tán int size = Sw.GetLength(0);
St = new double[size, size]; for (int i = 0; i < size; i++) for (int j = 0; j < size; j++)
// Tính toán tỉ lệ double sum = 0.0; sum += System.Math.Abs(eigenValues[i]); sum = (sum == 0) ? 0 : (1.0 / sum); for (int i = 0; i < numDiscriminants; i++) discriminantProportions[i] = System.Math.Abs(eigenValues[i]) * sum;
// Tính toán tỉ lệ dồn lại this.discriminantCumulative[0] = this.discriminantProportions[0]; for (int i = 1; i < this.discriminantCumulative.Length; i++) this.discriminantCumulative[i] = this.discriminantCumulative[i - 1] + this.discriminantProportions[i];
// Taọ cấu trúc hướng đối tượng để giữ biệt thức tuyến tính
Discriminant[] discriminants = new Discriminant[numDiscriminants]; for (int i = 0; i < numDiscriminants; i++) discriminants[i] = new Discriminant(this, i); this.discriminantCollection = new
# Phần hỗ trợ các lớp public class DiscriminantAnalysisClass
{ private LinearDiscriminantAnalysis analysis; private int classNumber; private int index; internal DiscriminantAnalysisClass(LinearDiscriminantAnalysis analysis, int index, int classNumber)
{ get { return (double)Count / analysis.Source.GetLength(0); }
/// Lấy lớp vecto trung bình public double[] Mean
{ get { return analysis.ClassMeans[index]; }
/// Lấy lớp vec tơ độ lệch quần phương public double[] StandardDeviation
{ get { return analysis.ClassStandardDeviations[index]; }
/// Lấy ma trận phân tán cho lớp này public double[,] Scatter
{ get { return analysis.ClassScatter[index]; }
} public int[] Indexes get { return Matrix.Find(analysis.Classifications, y => y = classNumber); }
{ return analysis.Source.Submatrix(Indexes);
{ get { return analysis.ClassCount[index]; }
/// Hàm biệt thức cho lớp public double DiscriminantFunction(double[] projection)
//return Mean.Multiply(projection) + Bias[index]; return analysis.discriminantFunction(index, projection);
{ private LinearDiscriminantAnalysis analysis; private int index; internal Discriminant(LinearDiscriminantAnalysis analysis, int index)
{ this.analysis = analysis; this.index = index;
{ get { return analysis.DiscriminantMatrix.GetColumn(index); }
{ get { return analysis.Eigenvalues[index]; }
{ get { return analysis.Proportions[index]; }
{ get { return analysis.CumulativeProportions[index]; }
} public class DiscriminantCollection : ReadOnlyCollection { internal DiscriminantCollection(Discriminant[] components)
3.2.2 Kernel Discrimnant Analysis using Accord.Math; using Accord.Math.Decompositions; using Accord.Statistics.Kernels; using System.Collections.Generic; namespace Accord.Statistics.Analysis
{ private IKernel kernel; private double regularization = 0.0001; private double threshold = 0.001; private double[][] kernelClassMeans;
# Phần kiến thiết tạo public KernelDiscriminantAnalysis(double[,] inputs, int[] output, IKernel kernel)
{ this.kernel = kernel; this.kernelClassMeans = new double[Classes.Count][];
# Phần khai báo thuộc tính public IKernel Kernel
{ get { return regularization; } set { regularization = value; }
{ get { return threshold; } set { threshold = value; }
# Phần khai báo Phương thức
/// Tính toán thuật toán KDA nhiều lớp public override void Compute()
// Lấy mốt số thông tin ban đầu int dimension = Source.GetLength(0); double[,] source = Source; double total = dimension;
// Tạo ma trận Gram (Kernel) for (int i = 0; i < dimension; i++)
{ double s = kernel.Function(source.GetRow(i), source.GetRow(j)); K[i, j] = s;
// Tính toán toàn bộ dữ liệu base.Means = Tools.Mean(K); base.StandardDeviations = Tools.StandardDeviation(K, Means);
// Gán giá trị ban đầu cho ma trận phân tán tương tự nhân double[,] Sb = new double[dimension, dimension]; double[,] Sw = new double[dimension, dimension];
// Cho mỗi lớp for (int c = 0; c < Classes.Count; c++)
// Lấy lớp con ma trận nhân double[,] Kc = K.Submatrix(Classes[c].Indexes); int count = Kc.GetLength(0);
// Lấy trung bình lớp ma trận Nhân double[] mean = Tools.Mean(Kc);
// Đặt ma trận tương đương của ma trận phân tán lớp trong double[,] Swi = Tools.Scatter(Kc, mean, (double)count);
// Sw = Sw + Swi for (int i = 0; i < dimension; i++)
// Đặt ma trận tương đương của ma trận phân tán lớp giữa double[] d = mean.Subtract(base.Means); double[,] Sbi = d.Multiply(d.Transpose()).Multiply(total);
// Sb = Sb + Sbi for (int i = 0; i < dimension; i++) for (int j = 0; j < dimension; j++)
// Lưu trữ dữ liệu thêm vào base.ClassScatter[c] = Swi; base.ClassCount[c] = count; base.ClassMeans[c] = mean; base.ClassStandardDeviations[c] = Tools.StandardDeviation(Kc, mean);
// Thêm quy tắc for (int i = 0; i < dimension; i++)
// Phân tích sự phân tán của giá trị riêng double[,] C = Matrix.Inverse(Sw).Multiply(Sb);
// Lấy giá trị riêng và tương ứng với vecsto riêng double[] evals = evd.RealEigenvalues; double[,] eigs = evd.Eigenvectors;
// Sắp xếp giá trị riêng và vecto riêng theo giá trị tăng dần eigs = Matrix.Sort(evals, eigs, new if (threshold > 0)
// Calculate proportions earlier double sum = 0.0; for (int i = 0; i < dimension; i++) sum += System.Math.Abs(evals[i]); if (sum > 0)
// Không lưu những thông tin không quan trọng int keep = 0; while (keep < dimension &&
System.Math.Abs(evals[keep]) * sum > threshold) keep++; eigs = eigs.Submatrix(0, dimension - 1, 0, keep - 1); evals = evals.Submatrix(0, keep - 1);
// Lưu trữ thông tin base.Eigenvalues = evals; base.DiscriminantMatrix = eigs; base.ScatterBetweenClass = Sb; base.ScatterWithinClass = Sw;
// Tính toán khoảng trống cho việc phân lớp cuối cùng for (int c = 0; c < Classes.Count; c++)
{ double[] mean = new double[eigs.GetLength(1)]; for (int i = 0; i < eigs.GetLength(0); i++) for (int j = 0; j < eigs.GetLength(1); j++)
// Tính toán các thông tin đƣợc thêm về việc phân tích và tạo
// cấu trúc hướng đối tượng để giữ các biệt thức tìm thấy createDiscriminants();
} public override double[,] Transform(double[,] data, int discriminants)
// Lấy một vài thông tin int rows = data.GetLength(0); int cols = data.GetLength(1); int N = Source.GetLength(0);
// Tạo ma trận nhân double[,] K = new double[rows, N]; for (int i = 0; i < rows; i++) for (int j = 0; j < N; j++)
K[i, j] = kernel.Function(Source.GetRow(j), data.GetRow(i));
// Xem xét khoảng trống biệt thức nhân double[,] result = new double[rows, discriminants]; for (int i = 0; i < rows; i++) for (int j = 0; j < discriminants; j++) for (int k = 0; k < N; k++) result[i, j] += K[i, k] * DiscriminantMatrix[k, j]; return result;
} internal override double discriminantFunction(int i, double[] projection) { return -Distance.SquareEuclidean(projection, kernelClassMeans[i]);