SQL Server 2005中數(shù)據(jù)挖掘算法擴展方法
SSAS為我們提供了九種數(shù)據(jù)挖掘算法,但是在應用中我們需要根據(jù)實際問題設計適當?shù)乃惴?,這個時候就需要擴展SSAS,使它能應用更多的算 法,SSAS有比較好的可擴展性,它提供了一個完整的機制來進行擴展,只要繼承一些類并按適當?shù)姆椒ㄟM行注冊就可以在SSAS中使用自己的算法了。
下面我將通過實例分別用幾篇文章來介紹一下如何開發(fā)SSAS算法插件。本文介紹的算法插件開發(fā)方法是基于托管代碼的,是用C#開發(fā)的(算法插件也可以用C++開發(fā),并且SQLSERVER2005的案例中附帶C++版本的代碼stub)。整個過程大至為六個步驟。在開始開發(fā)之前需要做一些準備工作,就是要去下載 一個用C++編寫的COM組件,叫DMPluginWrapper(可以通過下載本文附帶的附件獲得),它作為SSAS與算法插件的中間層,用于處理 SSAS與算法插件之間的交互以及封裝從SSAS到算法插件的參數(shù)和從算法插件到SSAS的處理結(jié)果。DMPluginWrapper、SSAS和算法插 件之間的關系可以由下圖來描述。
圖表 1: DMPluginWrapper、SSAS和算法插件之間的關系
下面開始創(chuàng)建算法擴展的項目。
首先新建一個類庫項目(名為AlgorithmPlugin)將剛才的DMPluginWrapper項目引用到新建的這個 AlgorithmPlugin類庫項目中。你可以選擇為這個類庫項目進行程序集簽名,這樣就可以將其注冊到GAC中。另外還要為 DMPluginWrapper添加后生成腳本將程序集注冊到GAC,參考腳本如下(根據(jù)機器具體設置而定):
"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\RegAsm.exe" $(TargetPath) |
如果***行腳本不能正確運行的話,算法插件是不能被SQLSERVER分析服務器識別的。另外兩行腳本就是將算法程序集注冊到GAC。
接下來的幾個步驟主要是繼承一些基類的工作,包括AlgorithmMetadataBase類、AlgorithmBase類和 ICaseProcessor接口和AlgorithmNavigationBase類。首先在AlgorithmPlugin中新建一個類文件并命名為 Metadata,為這個類添加ComVisible、MiningAlgorithmClass(typeof(Algorithm))和Guid屬性 (Algorithm是下面要創(chuàng)建的算法類),并為Guid屬性指定一個GUID編碼。這個類要繼承于AlgorithmMetadataBase類?,F(xiàn) 在要做的事情就是覆蓋基類的方法。下面是所有需要覆蓋的方法(對于較簡單的實現(xiàn)寫在表格中):
方法名實現(xiàn)(參考)備注
GetServiceName |
用于指定算法適用的規(guī)模,這個值不會被服務器使用而是顯示在模式行集中,為用戶提供算法的一些相關信息。
GetTrainingComplexity
return MiningTrainingComplexity.Low
用于指定算法訓練適用的復雜度,這個值不會被服務器使用而是顯示在模式行集中,為用戶提供算法的一些相關信息。
GetPredictionComplexity
return MiningPredictionComplexity.Low
用于指定預測復雜度,這個值不會被服務器使用而是顯示在模式行集中,為用戶提供算法的一些相關信息。
GetSupportsDMDimensions
retrun false;
GetSupportsDrillThrough
return false;
指定這個算法是否支持鉆透功能。
GetDrillThroughMustIncludeChildren
return false;
GetCaseIdModeled
return false;
GetMarginalRequirements
return MarginalRequirements.AllStats
GetParametersCollection
return null;
算法參數(shù),因為本文中的例子沒有參數(shù),所以這里返回空。
GetSupInputContentTypes
MiningColumnContent[] arInputContentTypes = new MiningColumnContent[]{
MiningColumnContent.Discrete,
MiningColumnContent.Continuous,
MiningColumnContent.Discretized,
MiningColumnContent.NestedTable,
MiningColumnContent.Key
};
return arInputContentTypes;
指定算法所支持的輸入屬性的數(shù)據(jù)類型,如連續(xù)型、離散型等。
GetSupPredictContentTypes
MiningColumnContent[] arPredictContentTypes = new MiningColumnContent[]{
MiningColumnContent.Discrete,
MiningColumnContent.Continuous,
MiningColumnContent.Discretized,
MiningColumnContent.NestedTable,
MiningColumnContent.Key
};
return arPredictContentTypes;
與上一個方法類似,這里是指定預測屬性所支持的數(shù)據(jù)類型。
GetSupportedStandardFunctions
SupportedFunction[] arFuncs= new SupportedFunction[] {
SupportedFunction.PredictSupport,
SupportedFunction.PredictHistogram,
SupportedFunction.PredictProbability,
SupportedFunction.PredictAdjustedProbability,
SupportedFunction.PredictAssociation,
SupportedFunction.PredictStdDev,
SupportedFunction.PredictVariance,
SupportedFunction.RangeMax,
SupportedFunction.RangeMid,
SupportedFunction.RangeMin,
SupportedFunction.DAdjustedProbability,
SupportedFunction.DProbability,
SupportedFunction.DStdDev,
SupportedFunction.DSupport,
SupportedFunction.DVariance,
// content-related functions
SupportedFunction.IsDescendent,
SupportedFunction.PredictNodeId,
SupportedFunction.IsInNode,
SupportedFunction.DNodeId,
};
return arFuncs;
指定DMX所支持的函數(shù)。
CreateAlgorithm
return new Algorithm();
返回算法實例,Algorithm是接下來要創(chuàng)建的類。
現(xiàn)在創(chuàng)建第二個類,命名為Algorithm.cs。這個類要繼承于AlgorithmBase并實現(xiàn)ICaseProcesses接口,這是實現(xiàn)算法最重要的一個類,主要的算法處理都在這個類中進行。這個類要有一個成員變量TaskProgressNotification trainingProgress。這個類包含了算法主要的處理邏輯。下面是要實現(xiàn)的方法:
方法名://處理樣本
InsertCases
參考實現(xiàn):
Code
//遍歷所有的樣本并且每處理100個樣本更新一次處理進度。
trainingProgress = this.Model.CreateTaskNotification();// 設置當前的處理進度為0
trainingProgress.Current = 0;// 取得總的樣本數(shù)量。
trainingProgress.Total =
(int)this.MarginalStats.GetTotalCasesCount();// 為跟蹤提示信息設置格式字符串
trainingProgress.Format = "Processing cases: {0} out of {1}";// 開始處理
trainingProgress.Start();
bool success = false;
try
{
caseSet.StartCases(this);
success = true;
}
finally
{
trainingProgress.End(success);
}
方法名:ProcessCase
參考實現(xiàn):
Code
// 檢查并確認處理過程沒有被中斷。
this.Context.CheckCancelled();// 更新當前的進度值
trainingProgress.Current++;if (caseId % 100 == 0)
{
trainingProgress.Progress();
}
//TODO: 在這里進行實際的模型訓練處理邏輯
方法名:SaveContent參考實現(xiàn):
Code
//創(chuàng)建一個自定義的標簽內(nèi)容用于保存處理結(jié)果(其結(jié)構(gòu)類似XML),MyPersistenceTag是自定義的枚舉類型writer.OpenScope((PersistItemTag)MyPersistenceTag.ShellAlgorithmContent);
writer.SetValue(System.DateTime.Now);
writer.SetAttribute((PersistItemTag)MyPersistenceTag.NumberOfCases,
this.MarginalStats.GetTotalCasesCount());
writer.CloseScope();
方法名:LoadContent
參考實現(xiàn):
Code
//打開自定義的標簽(與SaveContent方法相對應)
reader.OpenScope((PersistItemTag)MyPersistenceTag.ShellAlgorithmContent);//讀取處理時間
System.DateTime processingTime;
reader.GetValue(out processingTime);// 取得處理的樣本數(shù)量
uint numberCases = 0;reader.GetAttribute((PersistItemTag)MyPersistenceTag.NumberOfCases, out numberCases);
reader.CloseScope();
方法名:Predict
參考實現(xiàn):
Code
AttributeGroup targetAttributes = predictionResult.OutputAttributes;
targetAttributes.Reset();
uint nAtt = AttributeSet.Unspecified;//對于每一個目標屬性,從訓練集中復制預測結(jié)果
while (targetAttributes.Next(out nAtt))
{
//創(chuàng)建一個AttributeStatistics對象用于保存對當前目標屬性的預測結(jié)果
AttributeStatistics result = new AttributeStatistics();//設置預測結(jié)果中的目標屬性,即當前的預測結(jié)果針對于哪個輸入屬性
result.Attribute = nAtt;// 取得當前屬性的概率統(tǒng)計值,也即通過模型訓練得到的邊緣統(tǒng)計概率。
AttributeStatistics trainingStats = this.MarginalStats.GetAttributeStats(nAtt);//復制其余的數(shù)據(jù)到結(jié)果對象
result.AdjustedProbability = trainingStats.AdjustedProbability;
result.Max = trainingStats.Max;
result.Min = trainingStats.Min;
result.Probability = trainingStats.Probability;
result.Support = trainingStats.Support;//復制狀態(tài)統(tǒng)計到結(jié)果對象中
if (predictionResult.IncludeStatistics)
{
for ( int nIndex = 0; nIndex < trainingStats.StateStatistics.Count; nIndex++)
{
bool bAddThisState = true;// 如果是丟失值狀態(tài),那么只有當需要的時候才將其包含在結(jié)果之中。
if (trainingStats.StateStatistics[0].Value.IsMissing)
{
bAddThisState = predictionResult.IncludeMissingState;
}if (bAddThisState)
{
result.StateStatistics.Add(
trainingStats.StateStatistics[(uint)nIndex]);
}
}
}//如果預測需要內(nèi)容結(jié)點,就要為內(nèi)容結(jié)點設置一個唯一的編號
if (predictionResult.IncludeNodeId)
{
result.NodeId = "000";
}
predictionResult.AddPrediction(result);
方法名:GetNavigator參考實現(xiàn):
Code
//AlgorithmNavigator是下面要創(chuàng)建的類
return new AlgorithmNavigator(this, forDMDimensionContent);
接下來要實現(xiàn)的是AlgorithmNavigator類,這個類要繼承于 AlgorithmNavigationBase。這個類主要用于顯示算法處理結(jié)果中所有結(jié)點的信息。在這個類中有三個成員變量:Algorithm類型 的algorithm、bool類型的forDMDimension和int類型的currentNode。下面是這個類要實現(xiàn)的方法:
方法名(構(gòu)造方法):AlgorithmNavigator參考實現(xiàn):
Code
this.algorithm = currentAlgorithm;
this.forDMDimension = dmDimension;
this.currentNode = 0;
方法名
實現(xiàn)
備注
MoveToNextTree
return false;
GetCurrentNodeId
return currentNode;
ValidateNodeId
return (nodeId == 0);
LocateNode
if (!ValidateNodeId(nodeId))return false;
currentNode = nodeId;
return true;
GetNodeIdFromUniqueName
int nNode = Convert.ToInt32(nodeUniqueName);return nNode;
GetUniqueNameFromNodeId
return nodeId.ToString("D3");
按三位數(shù)字的格式輸出結(jié)點編號
GetParentCount
return 0;
GetParentNodeId
return 0;
GetChildrenCount
return 0
GetChildNodeId
return -1;
GetNodeType
return NodeType.Model;
GetNodeUniqueName
return GetUniqueNameFromNodeId(currentNode);
GetNodeAttributes
return null;
方法名:
//此方法返回了描述結(jié)點的數(shù)值特征
GetDoubleNodeProperty
參考實現(xiàn):
Code
double dRet = 0;
double dTotalSupport = lgorithm.MarginalStats.GetTotalCasesCount();
double dNodeSupport = 0.0;
dNodeSupport = dTotalSupport;
switch (property)
{
//結(jié)點的支持度
case NodeProperty.Support:
dRet = dNodeSupport;
break;
case NodeProperty.Score:
dRet = 0;
break;
//結(jié)點概率
case NodeProperty.Probability:
dRet = dNodeSupport / dTotalSupport;
break;
//結(jié)點的邊緣概率
case NodeProperty.MarginalProbability:
dRet = dNodeSupport / dTotalSupport;
break;
}
return dRet;
方法名://取得結(jié)點的字符串表示
GetStringNodeProperty
參考實現(xiàn):
Code
string strRet = "";
switch (property)
{
case NodeProperty.Caption:
{
strRet = algorithm.Model.FindNodeCaption(GetNodeUniqueName());
if (strRet.Length == 0)
{
strRet = "All";
}
}
break;
case NodeProperty.ConditionXml:
strRet = "";
break;
case NodeProperty.Description:
strRet = "All Cases";
break;
case NodeProperty.ModelColumnName:
strRet = "";
break;
case NodeProperty.RuleXml:
strRet = "";
break;
case NodeProperty.ShortCaption:
strRet = "All";
break;
}
return strRet;
方法名:
//取得結(jié)點的分布
GetNodeDistribution
參考實現(xiàn):
Code
int attStats = (int)algorithm.AttributeSet.GetAttributeCount();
AttributeStatistics[] marginalStats = new AttributeStatistics[attStats];
for (uint nIndex = 0; nIndex < attStats; nIndex++)
{
marginalStats[nIndex] = algorithm.MarginalStats.GetAttributeStats(nIndex);
}
return marginalStats;
現(xiàn)在我們已經(jīng)實現(xiàn)了完所有需要要實現(xiàn)的類,***要做的就是將算法插件部署到分析服務器。在完成代碼后,需要將程序集注冊到GAC以便分析服務器 可以從中加載插件。下面的代碼就是將DMPluginWrapper加載到GAC的腳本,如果在本文的開頭正確地在Visual Studio中設置了后生成(Post-Building)腳本的話可以跳過下面的腳本代碼,因為它們是相同的功能:
//將DMPluginWrapper.dll注冊到GAC中 |
注意以上的路徑一定要與自己機器的設置匹配。成功運行上面的腳本后算法插件就被注冊到計算機之中,但要在分析服務器中使用這個算法還有一項工 作,就是把在分析服務器中注冊這個算法。在分析服務器中注冊算法有兩種方式,一種是通過發(fā)送XMLA代碼到分析服務器來注冊;另一種是通過修改 SQLSERVER的配置文件來注冊算法插件。下面先說***種:
通過XMLA方式注冊算法插件——
<!--Template for registering a plug-in algorithm Replace MyPluginAlgorithm with the ServiceName of your algorithm Replace 00000000-0000-0000-0000-000000000000 with the Guid of your Algorithm After deploying, you will need to restart the server to load the plug-in --><ALTER AllowCreate="true" ObjectExpansion="ObjectProperties" xmlns="<A > > <BR><Server xmlns:xsd="<A <A > <Name>.</Name> <ServerProperties> <ServerProperty> <!-- 用自己的算法名稱替換 --> <Name>MyFirstAlgorithmPlugin</Name> <!-- 是否啟用算法 --> <Value>true</Value> </ServerProperty> <ServerProperty> <!-- 用自己的算法名稱替換 --> <BR><Name>MyFirstAlgorithmPlugin</Name> <!--算法的GUID(在算法類中指定的GUID) --><Value>00000000-0000-0000-0000-000000000000</Value> </ServerProperty> </ServerProperties></Server> </ObjectDefinition> </Alter> |
通過修改SQLSERVER的配置文件注冊算法——
找到SQLSERVER安裝目錄下的MSSQL.2\OLAP\Config\msmdsrv.ini文件。這個是一個XML格式的文檔。文檔內(nèi)容類似于下面的代碼:
<ConfigurationSettings> |
在以上的配置信息中算法名稱是來自于Metadata類的GetServiceName這個方法,即在配置中設置的算法名稱要與這個方法的返回值相同。到這里一個基本的算法插件程序就完成了,重啟分析服務器后就可以在建立挖掘模型的窗口中看到新的算法出現(xiàn)在算法下拉列表中了。
從建立算法插件的整個過程來看,除開前后的準備工作和部署主要就是對三個類的重寫,而其中最主要的類就是算法類的重寫,其它兩個類的主要作用是為算法提供元數(shù)據(jù)信息及結(jié)果的描述。在算法類中要實現(xiàn)挖掘模型的樣本訓練以及預測,這是算法最關鍵的地方,重寫算法類中的ProcessCase方法和Predict方法是算法插件的核心。在了解如何擴展算法后要做的工作就是設計新的算法或?qū)⒁呀?jīng)完成的算法集成到插件中。
【編輯推薦】