학부연구생

Adhoc network에서 노드들을 clustering하기.

Beige00 2024. 3. 26. 20:47

"Int J Communication - 2020 - Chaudhary - ORuML  Optimized Routing in wireless networks using Machine Learning" ,

Int J Communication - 2011 - Russell - Integrating machine learning in ad hoc routing  A wireless adaptive routing protocol

를 읽어보니, 노드들을 classification하겠다는 나의 아이디어가 완전히 허무맹랑한 이야기는 아니었던 것 같다.


 

이전까지 포스팅에서 시뮬레이션 데이터들을 구하는 것을 마무리했다.

그렇다면 이제 해당 데이터를 기반으로 노드를 clustering 해주는 아이디어를 고안해보겠다.

 

더보기
#include <sstream>
#include <fstream>
#include <ns3/network-module.h>
#include <ns3/core-module.h>
#include <ns3/applications-module.h>
#include <ns3/mobility-module.h>
#include <ns3/internet-module.h>
#include <ns3/yans-wifi-helper.h>
#include "ns3/energy-module.h"
#include "ns3/wifi-radio-energy-model-helper.h"
#include "ns3/device-energy-model.h"
#include "ns3/adhoc-wifi-mac.h"
#include "ns3/aodv-helper.h"
#include <regex>
#include <cmath>
#include <vector>
#include <string>

#define TxRange 250
#define TotalTime 250
#define nodeNum 30
#define sinkNum 5
#define dataMode "VhtMcs8"
#define phyMode "OfdmRate54Mbps"

using namespace ns3;

int ja=0;



int ios = 0;
int PDC[nodeNum];
int a[nodeNum];

void 
makeCsvFile(const std::vector<std::vector<std::string>>& data,std::ofstream* file1){
    for(const auto& row : data){
        for(auto it = row.begin(); it!=row.end(); ++it){
            for(char c : *it){
                file1->put(c);
            }
            if(next(it)!=row.end()){
                file1->put(',');
            }
        }
        file1->put('\n');
    }
}

Vector 
Normalize(const Vector& velocity){
    double magnitude = std::sqrt(velocity.x * velocity.x + velocity.y * velocity.y + velocity.z * velocity.z);
    // 크기가 0인 벡터는 정규화할 수 없으므로, 원본 벡터를 반환
    if (magnitude == 0)
    {
        return velocity;
    }
    return ns3::Vector(velocity.x / magnitude, velocity.y / magnitude, velocity.z / magnitude);
}

double distance(double x1, double y1, double x2, double y2){
    return sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));
}

double CalculateRelativeSpeed(double vAx, double vAy, double vBx, double vBy) {
    // 상대 속도 벡터의 각 성분을 계산
    double relativeVx = vBx - vAx;
    double relativeVy = vBy - vAy;

    return sqrt(pow(relativeVx, 2) + pow(relativeVy, 2));
}

void PhyRxDrop(std::string context, Ptr<const Packet> packet,WifiPhyRxfailureReason reason){
    std::regex rege("/NodeList/(\\d+)/");
    std::smatch match;
    std::regex_search(context,match,rege);
    int index = std::stoi(match[1].str());
    PDC[index]+=1;
}


void 
TracingParameter(NetDeviceContainer n, std::ofstream* os,std::vector<std::vector<std::string>> Data){

    double variance[nodeNum]={};
    double nodeX[nodeNum];
    double nodeY[nodeNum];
    double spdX[nodeNum];
    double spdY[nodeNum];
    int NN[nodeNum];
    int GNN[nodeNum];
    double mean1[nodeNum]={};

    
    
    //각 노드들의 데이터 긁어오기, 0=PacketSinkApp, 1=OnOffApp
    for(int i=0; i<nodeNum; i++){
        //node들의 에너지 잔량,속도, 방향, 위치 정보 수집(Node x, Node y)
        Ptr<Node> node = n.Get(i)->GetNode();
        Ptr<MobilityModel> mobility = node->GetObject<MobilityModel>();
        ApplicationContainer app = node->GetApplication(0);

        Vector pos = mobility->GetPosition();
        Vector velocity = mobility->GetVelocity();

        
        nodeX[i] = pos.x;
        nodeY[i] = pos.y;
        spdX[i] = velocity.x;
        spdY[i] = velocity.y;
    }
    
    //Node Strength 계산
    for(int i=0; i<nodeNum; i++){
        double totalLET=0;
        int GNNN = 0;
        int NNodeNum = 0;
        double LET[nodeNum];
        std::fill(LET,LET+nodeNum,-1000);
        for(int j=0; j<nodeNum; j++){
            //처리해줘야할 노드만 처리
            if(i==j) continue;
            double dis = distance(nodeX[i],nodeY[i],nodeX[j],nodeY[j]);
            if(dis<=TxRange){
                NNodeNum++;
                if((spdX[i]*spdX[j]+spdY[i]*spdY[j])>=0){
                    GNNN++;
                }//가까워짐
                else{
                    LET[j] = dis / (CalculateRelativeSpeed(spdX[i],spdY[i],spdX[j],spdY[j]));
                }
            }
        }//이 시점에서 모든 node i 에 대한 LET 정보 갱신 완료.
        if(GNNN!=NNodeNum){
            for(int k=0; k<nodeNum; k++){
                if(LET[k] == -1000) continue;
                else totalLET += LET[k];
        }
        double mean = totalLET / (NNodeNum-GNNN);
        for(int k=0; k<nodeNum; k++){
            if(LET[k] == -1000) continue;
            variance[i] += (LET[k]-mean)*(LET[k]-mean);
        }
        variance[i] /= (NNodeNum-GNNN);
        variance[i] = sqrt(variance[i]);
        mean1[i]=mean;
        }
        GNN[i] = GNNN;
        NN[i] = NNodeNum;
    }



    //Write Info
    for(int i=0; i<nodeNum; i++){
        Data.push_back({std::to_string(Simulator::Now().GetSeconds()),
        std::to_string(i+1),
        std::to_string(nodeX[i]),
        std::to_string(nodeY[i]),
        std::to_string(mean1[i]),
        std::to_string(variance[i]),
        std::to_string(NN[i]),
        std::to_string(GNN[i]),
        std::to_string(abs(spdX[i])+abs(spdY[i])),
        std::to_string(PDC[i])
        });
    }
    std::fill(PDC,PDC+nodeNum,0);
    std::fill(a,a+nodeNum,0);

    Ptr<UniformRandomVariable> x = CreateObject<UniformRandomVariable>();
    x->SetAttribute("Min",DoubleValue(0.5));
    x->SetAttribute("Max",DoubleValue(1.5));
    

    makeCsvFile(Data,os);
    Data.clear();
    std::cout<<Simulator::Now().GetSeconds()<<std::endl;
    Simulator::Schedule(Seconds(x->GetValue()),&TracingParameter,n,os,Data);
}


int 
main(int argc, char* argv[]){

    for(ja = 0; ja <100; ja++){
        std::ostringstream oss1;
        oss1<<"Result"<<ja<<".csv";

        std::ofstream file1 (oss1.str());
        std::vector<std::vector<std::string>> Data;

        Ptr<UniformRandomVariable> x = CreateObject<UniformRandomVariable>();
        x->SetAttribute("Min",DoubleValue(0));
        RngSeedManager::SetSeed(ja+1);
        RngSeedManager::SetRun(10);

    
    Data.push_back({"Second","NodeID","X","Y","AverageLET","Variance","NNeighbor","GoodNeighbor","Speed","PacketDrop"});
    

    NodeContainer AdhocNodes;
    AdhocNodes.Create(nodeNum);

    NetDeviceContainer AdhocDevices;

    MobilityHelper mobility;
    ObjectFactory points;
    points.SetTypeId("ns3::RandomRectanglePositionAllocator");
    points.Set("X",StringValue("ns3::UniformRandomVariable[Min=-350.0|Max=350.0]"));
    points.Set("Y",StringValue("ns3::UniformRandomVariable[Min=-350.0|Max=350.0]"));
    Ptr<PositionAllocator> waypos = points.Create() -> GetObject<PositionAllocator>();

    mobility.SetMobilityModel("ns3::RandomWaypointMobilityModel",
    "Speed",StringValue("ns3::UniformRandomVariable[Min=0.0|Max=10]"),
    "Pause",StringValue("ns3::UniformRandomVariable[Min=0.0|Max=2]"),
    "PositionAllocator",PointerValue(waypos));

    mobility.SetPositionAllocator("ns3::RandomRectanglePositionAllocator",
    "X",StringValue("ns3::UniformRandomVariable[Min=-350.0|Max=350.0]"),
    "Y",StringValue("ns3::UniformRandomVariable[Min=-350.0|Max=350.0]"));

    mobility.Install(AdhocNodes);
    //속도 pause time 방향 범위 내 랜덤.



    WifiHelper wifi;
    wifi.SetStandard(WIFI_STANDARD_80211ac);
    YansWifiPhyHelper phy;
    YansWifiChannelHelper channel;
    phy.EnableAscii("my-wifi-trace-file",AdhocNodes);


    WifiMacHelper mac;
    mac.SetType("ns3::AdhocWifiMac");
    phy.SetErrorRateModel("ns3::YansErrorRateModel");
    channel.SetPropagationDelay("ns3::ConstantSpeedPropagationDelayModel");
    wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager","DataMode",StringValue(dataMode),"ControlMode",StringValue(phyMode));
    wifi.ConfigHtOptions("ShortGuardIntervalSupported", BooleanValue(true));
    channel.AddPropagationLoss("ns3::RangePropagationLossModel","MaxRange",DoubleValue(TxRange));
    phy.SetChannel(channel.Create());
    AdhocDevices.Add(wifi.Install(phy,mac,AdhocNodes));

    InternetStackHelper internet;
    AodvHelper aodv;
    internet.SetRoutingHelper(aodv);
    internet.Install(AdhocNodes);
    
    Ipv4AddressHelper ipv4;
    ipv4.SetBase("10.1.1.0","255.255.255.0");
    Ipv4InterfaceContainer ipv4Interface = ipv4.Assign(AdhocDevices);

    uint64_t port = 9;
    ApplicationContainer sink_app;

    for(int i=0; i<sinkNum; i++){
        PacketSinkHelper packetSink("ns3::UdpSocketFactory",InetSocketAddress(Ipv4Address::GetAny(),port));
        sink_app.Add(packetSink.Install(AdhocNodes.Get(i)));
    }
    sink_app.Start(Seconds(0.0));
    sink_app.Stop(Seconds(TotalTime));

    
    for(int i=0; i<sinkNum; i++){
        for(int j=0; j<nodeNum; j++){
            if(i==j) continue;
            InetSocketAddress sinkSocket (ipv4Interface.GetAddress(i),port);
            OnOffHelper onoff("ns3::UdpSocketFactory",sinkSocket);
            onoff.SetAttribute("StartTime",TimeValue(Seconds(3)));
            onoff.SetAttribute("StopTime",TimeValue(Seconds(TotalTime)));
            onoff.SetAttribute("OnTime", StringValue("ns3::UniformRandomVariable[Min=0.5|Max=1.5]")); // 전송 지속 시간
            onoff.SetAttribute("OffTime", StringValue("ns3::UniformRandomVariable[Min=0|Max=1]")); // 전송 중단 시간
            onoff.SetAttribute("DataRate", DataRateValue(5000)); // 데이터 전송률
            onoff.Install(AdhocNodes.Get(j));
        }
    }


    Simulator::Stop(Seconds(TotalTime));
    
    for(int i=0; i<nodeNum; i++){
        std::ostringstream oss;
        oss<<"/NodeList/"<<i<<"/DeviceList/0/$ns3::WifiNetDevice/Phys/0/PhyRxDrop";
        Config::Connect (oss.str(), MakeBoundCallback (&PhyRxDrop));
    }

    Simulator::Schedule(Seconds(3),&TracingParameter,AdhocDevices,&file1,Data);
    Simulator::Run();
    Simulator::Destroy();


    file1.close();

    }
}

 

해당 코드로 250초 씩 100번의 시뮬레이션을 했다.

80개를 train, 20개를 test 데이터로 써볼 것이다.

 

우선, 뽑아낸 데이터는 3~250초 동안 찍힌 노드 30개의 정보이다.

 

이를 다음의 과정으로 Decision Tree 학습법에 적용시켰다.

#result 파일을 하나로 합침
import pandas as pd

#모든 파일을 합치기 위한 데이터 프레임 초기화
all_data = pd.DataFrame()

for file_id in range(0,100):
  file_path = f'/content/result{file_id}.csv'

  #각 파일마다 식별자 새기기
  try:
    temp_data = pd.read_csv(file_path)
    temp_data['FileID'] = file_id
    all_data = pd.concat([all_data, temp_data], ignore_index=True)
  except FileNotFoundError:
        print(f"File not found: {file_path}")

all_data.to_csv('all_data.csv', index=False)
all_data.info()

#File_id 기분으로 400개를 Training, 100개를 test
#DT Feature importance 추출, y-val = Packet Drop
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier

# 'File_id'를 기준으로 데이터 분할
unique_file_ids = all_data['FileID'].unique()
train_file_ids, test_file_ids = train_test_split(unique_file_ids, train_size=80, random_state=42)

# 훈련 데이터와 테스트 데이터 선택
train_data = all_data[all_data['FileID'].isin(train_file_ids)]
test_data = all_data[all_data['FileID'].isin(test_file_ids)]


# 특성과 타겟 변수 분리
X_train = train_data.drop(['Second','NodeID','PacketDrop', 'FileID'], axis=1)
y_train = train_data['PacketDrop']
X_test = test_data.drop(['Second','NodeID','PacketDrop', 'FileID'], axis=1)
y_test = test_data['PacketDrop']

# Decision Tree 모델 훈련
dt = DecisionTreeClassifier(max_depth=10,min_samples_split=20,
    min_samples_leaf=10, random_state=42)
dt.fit(X_train, y_train)

train_accuracy = dt.score(X_train, y_train)
test_accuracy = dt.score(X_test, y_test)

# 특성 중요도 추출
feature_importances = dt.feature_importances_

# 모델의 훈련 세트와 테스트 세트에서의 정확도 출력
print(f"\nDecision Tree Model Accuracy on Training Set: {train_accuracy:.4f}")
print(f"Decision Tree Model Accuracy on Test Set: {test_accuracy:.4f}")

# 특성 중요도와 해당 특성 이름을 매핑
feature_importance_dict = dict(zip(X_train.columns, dt.feature_importances_))

# 중요도 순으로 특성 정렬
sorted_features = sorted(feature_importance_dict.items(), key=lambda item: item[1], reverse=True)

# 정렬된 특성과 중요도 출력
print("Feature importances:")
for feature, importance in sorted_features:
    print(f"{feature}: {importance:.4f}")

 

Feature importances: NNeighbor: 0.8158 Y: 0.0443 X: 0.0432 AverageLET: 0.0350 Variance: 0.0274 Speed: 0.0268 GoodNeighbor: 0.0075

결과는 다음과 같다. NNeighbor의 중요도가 너무 높게 나온다.

예상되는 문제점은 다음과 같다.

 

1. PhyRxDrop을 사용하였음. => MacRxDrop으로 바꾸어야 프로토콜의 성능을 평가하는데 더 옳은 지표가 될 것이다.

2. NNeighbor가 많으면 PacketDrop이 많은건 당연함. 그냥 내가 이웃이 많으니까 경로로 사용될 경우가 많을 것이 당연하다.

=> 따라서 우선 MacRxDrop으로 바꾸어서 평가 후, 결과가 달라지지 않으면 PacketLossRatio를 만들도록 ns3를 바꾸어줘야할 것 같다.