2012年6月19日 星期二

Transact-SQL 倒傳遞類神經網路範例程式 (T-SQL BackPropagation Neural Network Sample)


以倒傳遞類神經網路來解決傳統的 XOR 問題。本套程式共包括鏈結值資料庫、學習程式、回憶程式三部份 --


  • 加權值資料庫 :儲存各類已計算出的加權值 (Weight) 資料。
  • 學習程式 (for Learning Phase) :此程式會依輸入數值及預期結果,計算出該類資料的各種鏈結值,並儲存於上述資料庫中;下次學習或回憶功能時便可使用。
  • 回憶程式 (for Retrieving Phase) :依照資料庫中存在的鏈結值,推算出欲查詢的資料。
 

加權值資料庫 (Weight Database)

if object_id('TEST') is null   
   create database TEST
go

use TEST
go

if object_id('Weight_Input_Hidden') is not null   
   drop table Weight_Input_Hidden

if object_id('Weight_Hidden_Output') is not null   
   drop table weight_hidden_output

create table Weight_Input_Hidden (
        IH_Group_ID    int,
        IH_No          int,
        I_No           int,
        H_No           int,
        Weight_Value   float
        constraint PK_Weight_Input_Hidden primary key (IH_Group_ID, IH_No)
        )

create table Weight_Hidden_Output (
        HO_Group_ID    int,
        HO_No          int,
        H_No           int,
        O_No           int,
        Weight_Value   float
        constraint PK_Weight_Hidden_Output primary key (HO_Group_ID, HO_No)
        )
go        


學習程式 (Learning Phase) 

於 TEST 資料庫中 Compile 以下程式碼,再依程式碼第 20 ~ 23 行的說明執行四次學習程序。

Create Proc usp_BPN_Learning @prm_input_item_1      int,
                             @prm_input_item_2      int,
                             @prm_expecting_value   int,
                             @prm_eta               float
/*-----------------------------------------------------------------------------------+
 | Author: Wei-jie Yang                                                              |
 | Date:   2012/06/19                                                                |
 | All Rights Reserved                                                               |
 |                                                                                   |
 | [Description]                                                                     |
 |  Backpropagation training program for XOR problem                                 |
 |                                                                                   |
 | [Parameters]                                                                      |
 |     - @prm_input_item_1             Training Value No.1                           |
 |     - @prm_input_item_2             Training Value No.2                           |
 |     - @prm_expecting_value          Training Target Value                         |
 |     - @prm_eta:                     Learning Rate                                 |
 |                                                                                   |
 | [Usage]                                                                           |
 |   (1). exec usp_BPN_Learning 1, 1, 1, 0.5                                         |
 |   (2). exec usp_BPN_Learning 0, 0, 1, 0.5                                         |
 |   (3). exec usp_BPN_Learning 1, 0, 0, 0.5                                         |
 |   (4). exec usp_BPN_Learning 0, 1, 0, 0.5                                         |   
 +-----------------------------------------------------------------------------------*/
as

declare @current_ho_weight         numeric(38, 2),
        @current_ih_weight         numeric(38, 2),
        @delta_1                   float,
        @delta_2                   float,
        @delta_3                   float,
        @exec_counter              int,
        @exit_main_procedure       char(1),
        @group_id                  varchar(10),
        @h                         int,
        @hidden_item_amount        int,
        @hidden_value              float,
        @ho_weight_1               float,
        @ho_weight_2               float,
        @ho_weight_matched         char(1),
        @i                         int,
        @i_value                   float,
        @ih_weight_matched         char(1),
        @input_item_amount         int,
        @o                         int,
        @ones                      int,
        @output_item_amount        int,
        @output_value              float,
        @pre_ho_weight             numeric(38, 2),
        @pre_ih_weight             numeric(38, 2),
        @record_counter            int,
        @stop_value                float,
        @temp_hidden_value         float,
        @temp_output_value         float,
        @total_exec_times          int,
        @Y1                        float,
        @Y2                        float,
        @zeros                     int
 
declare @input_layer table (
        i_no           int,
        i_name         varchar(20),
        i_value        float
        )

declare @hidden_layer table (
        h_no           int,
        h_name         varchar(20),
        h_value        float              
        )

declare @weight_input_hidden table (             -- Weights between Input & Hidden Layers
        ih_group_id    int,
        ih_no          int,
        i_no           int,
        h_no           int,
        weight_value   float
        )

declare @temp_weight_input_hidden table (
        ih_group_id    int,
        ih_no          int,
        i_no           int,
        h_no           int,
        weight_value   float
        )

declare @weight_hidden_output table (            -- Weights between Hidden & Output Layers
        ho_group_id    int,
        ho_no          int,        
        h_no           int,
        o_no           int,
        weight_value   float
        )

declare @temp_weight_hidden_output table (
        ho_group_id    int,
        ho_no          int,        
        h_no           int,
        o_no           int,
        weight_value   float
        )


set nocount on


-- Initialization (begin) =============================================================================================
select @input_item_amount   = 2
select @hidden_item_amount  = 2
select @output_item_amount  = 1
select @total_exec_times    = 100

if @prm_input_item_1 = @prm_input_item_2
   select @group_id = 1
else
   select @group_id = 0


-- Get Weights (between Input & Hidden Layers) ----------------------------------------------------
select @record_counter = count(*)
  from Weight_Input_Hidden
 where IH_Group_ID = @group_id
  
if @record_counter > 0  
 begin
    insert into @weight_input_hidden (ih_group_id, ih_no, i_no, h_no, weight_value)
       select IH_Group_ID, IH_No, I_No, H_No, Weight_Value 
         from TEST.dbo.Weight_Input_Hidden
        where IH_Group_ID = @group_id
 end
else
 begin
    select @i = 1
 
    while @i <= @input_item_amount
     begin
        select @h = 1
    
        while @h <= @hidden_item_amount
         begin
         
            insert into @weight_input_hidden (ih_group_id, ih_no, i_no, h_no, weight_value) 
               values(@group_id, @i * 100 + @h, @i, @h, round(rand(), 2))

            select @h = @h + 1  
         end
         
        select @i = @i + 1
     end
 end


-- Get Weights (between Hidden & Output Layers) ---------------------------------------------------
select @record_counter = count(*)
  from Weight_Hidden_Output
 where HO_Group_ID = @group_id
      
if @record_counter > 0  
 begin
    insert into @weight_hidden_output (ho_group_id, ho_no, h_no, o_no, weight_value)
       select HO_Group_ID, HO_No, H_No, O_No, Weight_Value 
         from TEST.dbo.Weight_Hidden_Output
        where HO_Group_ID = @group_id
 end
else
 begin
    select @o = 1
    
    while @o <= @output_item_amount
     begin
        select @h = 1

        while @h <= @hidden_item_amount
         begin
            insert into @weight_hidden_output (ho_group_id, ho_no, h_no, o_no, weight_value) 
               values(@group_id, @h * 100 + @o, @h, @o, round(rand(), 2))

            select @h = @h + 1
         end
     
        select @o = @o + 1
     end
 end
-- Initialization (end) ===============================================================================================



-- Generate Input Items (begin) =======================================================================================
insert into @input_layer (i_no, i_name, i_value) values(1, 'X1', @prm_input_item_1)
insert into @input_layer (i_no, i_name, i_value) values(2, 'X2', @prm_input_item_2)
-- Generate Input Items (end) =========================================================================================



-- Main Procedure (begin) =============================================================================================
select @exec_counter = 1
select @output_value = 99
select @exit_main_procedure = 'N'


WHILE (@exec_counter <= @total_exec_times) and (@exit_main_procedure = 'N')
 BEGIN
   select @ih_weight_matched = 'N'
   select @ho_weight_matched = 'N'
 
   -- Generate Hidden Items (begin) ---------------------------------------------------------------
   delete from @hidden_layer
    where 1 = 1  

   select @h = 1

   while @h <= @hidden_item_amount
    begin
       select @temp_hidden_value = sum(i.i_value * w.weight_value)
         from @weight_input_hidden w,
              @input_layer i
        where w.i_no = i.i_no

       select @hidden_value = 1 / (1 + exp((-1) * @temp_hidden_value))

       delete from @hidden_layer
        where h_no = @h
 
       insert into @hidden_layer (h_no, h_name, h_value)
          values (@h, 'Y' + cast(@h as varchar(10)), @hidden_value)
 
       select @h = @h + 1
    end
   -- Generate Hidden Items (end) -----------------------------------------------------------------


   -- Generate Output Item (begin) ----------------------------------------------------------------
   select @temp_output_value = sum(h_value * w.weight_value)
     from @weight_hidden_output w,
          @hidden_layer h
    where w.h_no = h.h_no

   select @output_value = 1 / (1 + exp((-1) * @temp_output_value))
   -- Generate Output Item (end) ------------------------------------------------------------------


   -- Delta Calculation (begin) -------------------------------------------------------------------
   select @delta_3 = (@prm_expecting_value - @output_value) * @output_value * (1 - @output_value)

   select @Y1 = h_value
     from @hidden_layer
    where h_no = 1

   select @Y2 = h_value
     from @hidden_layer
    where h_no = 2

   select @ho_weight_1 = weight_value
     from @weight_hidden_output
    where h_no = 1

   select @ho_weight_2 = weight_value
     from @weight_hidden_output
    where h_no = 2
 
   select @delta_1 = @Y1 * (1 - @Y1) * @delta_3 * @ho_weight_1

   select @delta_2 = @Y2 * (1 - @Y2) * @delta_3 * @ho_weight_2
   -- Delta Calculation (end) ---------------------------------------------------------------------


   -- Adjustment (begin) --------------------------------------------------------------------------
   -- Input - Hidden Weights ........................................
   delete from @temp_weight_input_hidden
    where ih_group_id = @group_id

   insert into @temp_weight_input_hidden (ih_group_id, ih_no, i_no, h_no, weight_value)
      select w.ih_group_id, w.ih_no, w.i_no, w.h_no, w.weight_value + @prm_eta * @delta_1 * i.i_value
        from @input_layer i,
             @weight_input_hidden w
       where i.i_no = w.i_no
         and w.h_no = 1
         and w.ih_group_id = @group_id

   insert into @temp_weight_input_hidden (ih_group_id, ih_no, i_no, h_no, weight_value)
      select w.ih_group_id, w.ih_no, w.i_no, w.h_no, w.weight_value + @prm_eta * @delta_2 * i.i_value
        from @input_layer i,
             @weight_input_hidden w
       where i.i_no = w.i_no
         and w.h_no = 2
         and w.ih_group_id = @group_id

   select @pre_ih_weight = weight_value
     from @weight_input_hidden
    where ih_group_id = @group_id

   delete from @weight_input_hidden
    where ih_group_id = @group_id
 
   insert into @weight_input_hidden (ih_group_id, ih_no, i_no, h_no, weight_value)
      select ih_group_id, ih_no, i_no, h_no, weight_value
        from @temp_weight_input_hidden
       where ih_group_id = @group_id 
       order by ih_no
       
   select @current_ih_weight = weight_value
     from @weight_input_hidden   
    where ih_group_id = @group_id 
   
   if @pre_ih_weight = @current_ih_weight
      select @ih_weight_matched = 'Y'


   -- Hidden - Output Weights .......................................
   delete from @temp_weight_hidden_output
    where ho_group_id = @group_id
 
   insert into @temp_weight_hidden_output (ho_group_id, ho_no, h_no, o_no, weight_value)    
      select w.ho_group_id, w.ho_no, w.h_no, w.o_no, w.weight_value + @prm_eta * @delta_3 * h.h_value
        from @hidden_layer h,
             @weight_hidden_output w
       where h.h_no = w.h_no
         and ho_group_id = @group_id

   select @pre_ho_weight = weight_value
     from @weight_hidden_output
    where ho_group_id = @group_id
       
   delete from @weight_hidden_output
    where ho_group_id = @group_id
    
   insert into @weight_hidden_output (ho_group_id, ho_no, h_no, o_no, weight_value)
      select ho_group_id, ho_no, h_no, o_no, weight_value
        from @temp_weight_hidden_output
       where ho_group_id = @group_id
       order by ho_no
       
   select @current_ho_weight = weight_value
     from @weight_hidden_output       
    where ho_group_id = @group_id
     
   if @pre_ho_weight = @current_ho_weight
      select @ho_weight_matched = 'Y'
   -- Adjustment (end) ----------------------------------------------------------------------------


   if (@ih_weight_matched = 'Y') and (@ho_weight_matched = 'Y')
      select @exit_main_procedure = 'Y'

   select @exec_counter = @exec_counter + 1
 END
-- Main Procedure (end) ===============================================================================================



-- Save Result (begin) ================================================================================================
delete from TEST.dbo.Weight_Input_Hidden
 where IH_Group_ID = @group_id

insert into TEST.dbo.Weight_Input_Hidden (IH_Group_ID, IH_No, I_No, H_No, Weight_Value)
   select ih_group_id, ih_no, i_no, h_no, weight_value
     from @weight_input_hidden
    where ih_group_id = @group_id

delete from TEST.dbo.Weight_Hidden_Output
 where HO_Group_ID = @group_id

insert into TEST.dbo.Weight_Hidden_Output (HO_Group_ID, HO_No, H_No, O_No, Weight_Value)
   select ho_group_id, ho_no, h_no, o_no, weight_value
     from @weight_hidden_output
    where ho_group_id = @group_id     
-- Save Result (end) ==================================================================================================
 
set nocount off 

return


回憶程式 (Retrieving Phase) 

於 TEST 資料庫中 Compile 以下程式碼,再依程式碼第 18 ~ 20 行的說明執行回憶程序。
Create Proc usp_BPN_Retrieving @prm_input_item_1      int,
                               @prm_input_item_2      int,
                               @rtn_result            int output
/*---------------------------------------------------------------+
 | Author: Wei-jie Yang                                          |
 | Date:   2012/06/19                                            |
 | All Rights Reserved                                           |
 |                                                               |
 | [Description]                                                 |
 |  Backpropagation training program for XOR problem             |
 |                                                               |
 | [Parameters]                                                  |
 |     - @prm_input_item_1:   Input Value No.1                   |
 |     - @prm_input_item_2:   Input Value No.2                   |
 |     - @rtn_result:         Result                             |
 |                                                               |
 | [Usage]                                                       |
 |   declare @result   int                                       |
 |   exec usp_BPN_Retrieving 1, 1, @result output                |
 |   select @result                                              |
 +---------------------------------------------------------------*/
as

declare @group_id                  varchar(10),
        @h                         int,
        @hidden_item_amount        int,
        @hidden_value              float,
        @i                         int,
        @input_item_amount         int,
        @o                         int,
        @output_item_amount        int,
        @output_value              float,
        @record_counter            int,
        @temp_hidden_value         float,
        @temp_output_value         float
 
declare @input_layer table (
        i_no           int,
        i_name         varchar(20),
        i_value        float
        )

declare @hidden_layer table (
        h_no           int,
        h_name         varchar(20),
        h_value        float              
        )

declare @weight_input_hidden table (             -- Weights between Input & Hidden Items
        ih_group_id    int,
        ih_no          int,
        i_no           int,
        h_no           int,
        weight_value   float
        )

declare @temp_weight_input_hidden table (
        ih_group_id    int,
        ih_no          int,
        i_no           int,
        h_no           int,
        weight_value   float
        )

declare @weight_hidden_output table (            -- Weights between Hidden & Output Items
        ho_group_id    int,
        ho_no          int,        
        h_no           int,
        o_no           int,
        weight_value   float
        )

declare @temp_weight_hidden_output table (
        ho_group_id    int,
        ho_no          int,        
        h_no           int,
        o_no           int,
        weight_value   float
        )


set nocount on


-- Initialization (begin) =============================================================================================
select @input_item_amount   = 3
select @hidden_item_amount  = 3
select @output_item_amount  = 1

if @prm_input_item_1 = @prm_input_item_2
   select @group_id = 1
else
   select @group_id = 0


-- Get Weights (between Input & Hidden Layers) ----------------------------------------------------
select @record_counter = count(*)
  from Weight_Input_Hidden
 where IH_Group_ID = @group_id
  
if @record_counter > 0  
 begin
    insert into @weight_input_hidden (ih_group_id, ih_no, i_no, h_no, weight_value)
       select IH_Group_ID, IH_No, I_No, H_No, Weight_Value 
         from TEST.dbo.Weight_Input_Hidden
        where IH_Group_ID = @group_id
 end
else
 begin
    select @i = 0
 
    while @i <= @input_item_amount - 1
     begin
        select @h = 1
    
        while @h <= @hidden_item_amount - 1
         begin
         
            insert into @weight_input_hidden (ih_group_id, ih_no, i_no, h_no, weight_value) 
               values(@group_id, @i * 100 + @h, @i, @h, round(rand(), 2))

            select @h = @h + 1  
         end
         
        select @i = @i + 1
     end
 end


-- Get Weights (between Hidden & Output Layers) ---------------------------------------------------
select @record_counter = count(*)
  from Weight_Hidden_Output
 where HO_Group_ID = @group_id
      
if @record_counter > 0  
 begin
    insert into @weight_hidden_output (ho_group_id, ho_no, h_no, o_no, weight_value)
       select HO_Group_ID, HO_No, H_No, O_No, Weight_Value 
         from TEST.dbo.Weight_Hidden_Output
        where HO_Group_ID = @group_id
 end
else
 begin
    select @o = 1
    
    while @o <= @output_item_amount
     begin
        select @h = 0

        while @h <= @hidden_item_amount - 1
         begin
            insert into @weight_hidden_output (ho_group_id, ho_no, h_no, o_no, weight_value) 
               values(@group_id, @h * 100 + @o, @h, @o, round(rand(), 2))

            select @h = @h + 1
         end
     
        select @o = @o + 1
     end
 end
-- Initialization (end) ===============================================================================================



-- Generate Input Items (begin) =======================================================================================
insert into @input_layer (i_no, i_name, i_value) values(0, 'X0', -1)
insert into @input_layer (i_no, i_name, i_value) values(1, 'X1', @prm_input_item_1)
insert into @input_layer (i_no, i_name, i_value) values(2, 'X2', @prm_input_item_2)
-- Generate Input Items (end) =========================================================================================



-- Main Procedure (begin) =============================================================================================
select @output_value = 99


-- Generate Hidden Items (begin) ---------------------------------------------------------------
delete from @hidden_layer
where 1 = 1  

insert into @hidden_layer (h_no, h_name, h_value)
  values(0, 'X0', -1)

select @h = 1

while @h <= @hidden_item_amount - 1
begin
   select @temp_hidden_value = sum(i.i_value * w.weight_value)
     from @weight_input_hidden w,
          @input_layer i
    where w.i_no = i.i_no

   select @hidden_value = 1 / (1 + exp((-1) * @temp_hidden_value))

   delete from @hidden_layer
    where h_no = @h

   insert into @hidden_layer (h_no, h_name, h_value)
      values (@h, 'Y' + cast(@h as varchar(10)), @hidden_value)

   select @h = @h + 1
end
-- Generate Hidden Items (end) -----------------------------------------------------------------


-- Generate Output Item (begin) ----------------------------------------------------------------
select @temp_output_value = sum(h_value * w.weight_value)
 from @weight_hidden_output w,
      @hidden_layer h
where w.h_no = h.h_no

select @output_value = 1 / (1 + exp((-1) * @temp_output_value))
-- Generate Output Item (end) ------------------------------------------------------------------


if @output_value < 0.5
   select @output_value = 0
else
   select @output_value = 1
   
select @rtn_result = @output_value
   
set nocount off

return