最近在項目中,為了演算法結果的可視化,需要用到混淆矩陣(Confusion Matrix),而網上資源大多是基於Python繪製的混淆矩陣,並且是輸出圖片格式,並不能響應用戶點擊,今天以一個簡單的小例子,簡述如何通過WPF繪製混淆矩陣,並可響應用戶點擊事件,僅供學習分享使用,如有不足之處,還請指正。 ...
最近在項目中,為了演算法結果的可視化,需要用到混淆矩陣(Confusion Matrix),而網上資源大多是基於Python繪製的混淆矩陣,並且是輸出圖片格式,並不能響應用戶點擊,今天以一個簡單的小例子,簡述如何通過WPF繪製混淆矩陣,並可響應用戶點擊事件,僅供學習分享使用,如有不足之處,還請指正。
什麼是混淆矩陣?
在機器學習中, 混淆矩陣是一個誤差矩陣, 常用來可視化地評估監督學習演算法的性能. 混淆矩陣大小為 (n_classes, n_classes) 的方陣, 其中 n_classes 表示類的數量. 這個矩陣的每一行表示真實類中的實例, 而每一列表示預測類中的實例 (Tensorflow 和 scikit-learn 採用的實現方式). 也可以是, 每一行表示預測類中的實例, 而每一列表示真實類中的實例 (Confusion matrix From Wikipedia 中的定義). 通過混淆矩陣, 可以很容易看出系統是否會弄混兩個類, 這也是混淆矩陣名字的由來.
混淆矩陣是一種特殊類型的列聯表(contingency table)或交叉製表(cross tabulation or crosstab). 其有兩維 (真實值 "actual" 和 預測值 "predicted" ), 這兩維都具有相同的類("classes")的集合. 在列聯表中, 每個維度和類的組合是一個變數. 列聯表以表的形式, 可視化地表示多個變數的頻率分佈.
對於應用程式開發人員而言,混淆矩陣就是一個二維數組,分別表示預測值和真實值,裡面的值表示對應值的占比。
開發步驟
創建WPF應用程式項目
在瞭解了混淆矩陣的用途和原理後,就可以著手去開發,首先創建一個WPF應用程式項目,然後創建模型MatrixM,主要包括標題,x軸,y軸的標簽和刻度說明,數據,顏色設置。如下所示:
public class MatrixM:ObservableObject
{
private string title;
public string Title { get { return title; } set { SetProperty(ref title, value); } }
private string xLabel;
public string XLabel { get { return xLabel; } set { SetProperty(ref xLabel, value); } }
private string yLabel;
public string YLabel { get { return yLabel; } set { SetProperty(ref yLabel,value); } }
private string[] yaxis;
public string[] Yaxis { get { return yaxis; } set { SetProperty(ref yaxis, value); } }
private string[] xaxis;
public string[] Xaxis { get { return xaxis; } set { SetProperty(ref xaxis, value); } }
public double[,] Data { get; set; }
private Color minBrush;
public Color MinBrush { get { return minBrush; } set { SetProperty(ref minBrush, value); } }
private Color maxBrush;
public Color MaxBrush { get { return maxBrush; } set { SetProperty(ref maxBrush, value); } }
}
註意:在本示例中,矩陣的數據採用二維數組進行存儲。
構造數據
構造示例數據,在實際開發中,數據來源於演算法的真實分析,本例主要用於演示前端開發,所以構造一些測試數據,如下所示:
private MatrixM dataM;
public MatrixM DataM { get { return dataM; } set { SetProperty(ref dataM,value); } }
private UniformGrid matrix;
public MainWindowViewModel()
{
this.DataM = new MatrixM();
this.DataM.Title = "Confusion Matrix on Fer2024";
this.DataM.XLabel = "Predict Label";
this.DataM.YLabel = "Truth Label";
this.DataM.Xaxis = new string[] { "Angry","Disgust","Fear","Happy","Sad","Surprise","Neutral"};
this.DataM.Yaxis = new string[] { "Angry", "Disgust", "Fear", "Happy", "Sad", "Surprise", "Neutral" };
this.DataM.MinBrush = Colors.White;
this.DataM.MaxBrush= Colors.DarkSlateBlue;
this.DataM.Data = new double[,] {
{0.66,0.01,0.09,0.04,0.11,0.01,0.09 },
{0.23,0.64,0.0,0.04,0.09,0.0,0.0},
{0.08,0.0,0.58,0.02,0.15,0.08,0.1},
{0.01,0.0,0.01,0.89,0.01,0.02,0.06 },
{0.09,0.0,0.11,0.03,0.6,0.01,0.15},
{0.02,0.0,0.05,0.04,0.02,0.85,0.02 },
{0.05,0.0,0.04,0.07,0.11,0.01,0.72 }
};
}
頁面佈局
在WPF中,為了彈性呈現數據及自動縮放,主要用Grid,UniformGrid進行頁面佈局,交互主要用Button來實現,都是基礎知識。如下所示:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Text="{Binding DataM.Title}" FontSize="20" FontWeight="Bold" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5"></TextBlock>
<TextBlock Text="{Binding DataM.YLabel}" Grid.Row="1" Grid.Column="0" FontWeight="Bold" FontSize="14" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5">
<TextBlock.LayoutTransform>
<RotateTransform Angle="270"></RotateTransform>
</TextBlock.LayoutTransform>
</TextBlock>
<Grid Grid.Row="1" Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<ItemsControl Grid.Row="0" Grid.Column="0" Grid.RowSpan="1" ItemsSource="{Binding DataM.Yaxis}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="1"></UniformGrid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Right">
<TextBlock Text="{Binding}"></TextBlock>
<Border Width="10" Height="1" BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Right" Margin="10 0 0 0"></Border>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Border BorderBrush="Black" BorderThickness="1" Grid.Row="0" Grid.Column="1" >
<UniformGrid x:Name="matrix" >
<UniformGrid.Resources>
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Opacity" Value="0.5"></Setter>
<Setter Property="FontWeight" Value="Bold"></Setter>
<Setter Property="FontSize" Value="16"></Setter>
<Setter Property="Cursor" Value="Hand"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</UniformGrid.Resources>
</UniformGrid>
</Border>
<ItemsControl Grid.Row="1" Grid.Column="1" ItemsSource="{Binding DataM.Xaxis}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="1"></UniformGrid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" VerticalAlignment="Top" HorizontalAlignment="Center">
<Border Width="10" Height="1" BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Center" Margin="0 0 0 0">
<Border.RenderTransformOrigin>
<Point X="0.5" Y="0.5"></Point>
</Border.RenderTransformOrigin>
<Border.LayoutTransform>
<TransformGroup>
<RotateTransform Angle="90"></RotateTransform>
</TransformGroup>
</Border.LayoutTransform>
</Border>
<TextBlock Text="{Binding}">
<TextBlock.RenderTransformOrigin>
<Point X="0.5" Y="0.5"></Point>
</TextBlock.RenderTransformOrigin>
<TextBlock.LayoutTransform>
<TransformGroup>
<RotateTransform Angle="-45"></RotateTransform>
<TranslateTransform Y="20"></TranslateTransform>
</TransformGroup>
</TextBlock.LayoutTransform>
</TextBlock>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Rectangle Grid.Row="0" Grid.Column="2" Width="20" Margin="10 0">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0 0" EndPoint="1 1">
<GradientStop Offset="0" Color="{Binding DataM.MaxBrush}"></GradientStop>
<GradientStop Offset="1" Color="{Binding DataM.MinBrush}"></GradientStop>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
<TextBlock Text="{Binding DataM.XLabel}" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" FontWeight="Bold" FontSize="14" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5"></TextBlock>
</Grid>
註意:標題,刻度,軸說明 ,都是固定佈局,可以進行數據綁定,而矩陣內容需要動態創建,所以分開處理。
構造矩陣
在本示例中,混淆矩陣以UniformGrid為容器,動態根據數據創建,並填充到容器中,如下所示:
private IRelayCommand<object> loadedCommand;
public IRelayCommand<object> LoadedCommand =>loadedCommand??=new RelayCommand<object>(Loaded);
private void Loaded(object obj)
{
if (obj != null)
{
var win = obj as MainWindow;
if (win != null)
{
this.matrix = win.matrix;
InitMatrix();
}
}
}
private void InitMatrix()
{
if(this.matrix == null)
{
return;
}
this.matrix.Children.Clear();
this.matrix.Rows = this.DataM.Data.GetLength(0);
this.matrix.Columns = this.DataM.Data.GetLength(1);
var color = this.DataM.MaxBrush;
for(int row = 0; row < this.DataM.Data.GetLength(0); row++)
{
for(int col = 0; col < this.DataM.Data.GetLength(1); col++)
{
Border border = new Border();
border.Background = new SolidColorBrush(Color.FromArgb((byte)(this.DataM.Data[row, col] * color.A), color.R, color.G, color.B));
var button = new Button();
button.Content = this.DataM.Data[row, col].ToString("0.00");
button.FontSize = 12;
button.HorizontalContentAlignment= System.Windows.HorizontalAlignment.Center;
button.VerticalContentAlignment= System.Windows.VerticalAlignment.Center;
button.Background = Brushes.Transparent;
button.BorderThickness = new System.Windows.Thickness(0);
border.Child= button;
this.matrix.Children.Add(border);
}
}
}
註意,在Grid,UniformGrid此類容器中,控制項不需要設置寬和高,會自動根據容器大小進行自適應。且不能設置對齊屬性,否則控制項大小則不會自適應調整大小。
示例效果
運行VS,實例效果如下所示:
以上就是【基於WPF開發動態可交互混淆矩陣】的全部內容。希望可以一起學習,共同進步。
作者:小六公子
出處:http://www.cnblogs.com/hsiang/
本文版權歸作者和博客園共有,寫文不易,支持原創,歡迎轉載【點贊】,轉載請保留此段聲明,且在文章頁面明顯位置給出原文連接,謝謝。
關註個人公眾號,定時同步更新技術及職場文章