আরসিপিপি আস্তে আস্তে আরজিকাল ফাংশনটি অনুকূল করা কেন?


16

আমি বর্তমানে একটি বায়েশিয়ান পদ্ধতিতে কাজ করছি যার জন্য পুনরাবৃত্তির জন্য বহুজাতিক লজিট মডেলটির অনুকূলিতকরণের একাধিক পদক্ষেপ প্রয়োজন। আমি এই অপটিমাইজেশনগুলি সম্পাদন করতে অপ্টিম () ব্যবহার করছি এবং আর-তে লেখা একটি উদ্দেশ্যমূলক ফাংশনটি প্রকাশ করেছে যে সর্বোত্তম () হ'ল প্রধান বাধা।

চারপাশে খনন করার পরে, আমি এই প্রশ্নটি পেয়েছি যার মধ্যে তারা প্রস্তাব দেয় যে সাথে উদ্দেশ্যমূলক ফাংশনটি পুনরায় সাজানো Rcppপ্রক্রিয়াটির গতি বাড়িয়ে দিতে পারে। আমি পরামর্শটি অনুসরণ করেছি এবং আমার উদ্দেশ্যমূলক ক্রিয়াকলাপটি পুনরায় সংশোধন করেছি Rcpp, তবে এটি ধীরে ধীরে শেষ হয়েছে (প্রায় দুইগুণ ধীর!)।

এটি Rcpp(বা সি ++ এর সাথে সম্পর্কিত কিছু) এর সাথে আমার প্রথমবার ছিল এবং আমি কোডটি ভেক্টরাইজ করার কোনও উপায় খুঁজে পাইনি। এটি কীভাবে দ্রুত করা যায় কোনও ধারণা?

টিএল; ড: আরসিপিতে ফাংশনের বর্তমান প্রয়োগ ভেক্টরিযুক্ত আর এর মতো তত দ্রুত নয়; কিভাবে এটি দ্রুত করতে?

একটি পুনরুত্পাদনযোগ্য উদাহরণ :

1) উদ্দেশ্যমূলক ফাংশনগুলি সংজ্ঞায়িত করুন Rএবং Rcpp: কেবলমাত্র একটি ইন্টারসেপ্ট কেবল বহুজাতিক মডেলের লগ-সম্ভাবনা

library(Rcpp)
library(microbenchmark)

llmnl_int <- function(beta, Obs, n_cat) {
  n_Obs     <- length(Obs)
  Xint      <- matrix(c(0, beta), byrow = T, ncol = n_cat, nrow = n_Obs)
  ind       <- cbind(c(1:n_Obs), Obs)
  Xby       <- Xint[ind]
  Xint      <- exp(Xint)
  iota      <- c(rep(1, (n_cat)))
  denom     <- log(Xint %*% iota)
  return(sum(Xby - denom))
}

cppFunction('double llmnl_int_C(NumericVector beta, NumericVector Obs, int n_cat) {

    int n_Obs = Obs.size();

    NumericVector betas = (beta.size()+1);
    for (int i = 1; i < n_cat; i++) {
        betas[i] = beta[i-1];
    };

    NumericVector Xby = (n_Obs);
    NumericMatrix Xint(n_Obs, n_cat);
    NumericVector denom = (n_Obs);
    for (int i = 0; i < Xby.size(); i++) {
        Xint(i,_) = betas;
        Xby[i] = Xint(i,Obs[i]-1.0);
        Xint(i,_) = exp(Xint(i,_));
        denom[i] = log(sum(Xint(i,_)));
    };

    return sum(Xby - denom);
}')

2) তাদের দক্ষতা তুলনা:

## Draw sample from a multinomial distribution
set.seed(2020)
mnl_sample <- t(rmultinom(n = 1000,size = 1,prob = c(0.3, 0.4, 0.2, 0.1)))
mnl_sample <- apply(mnl_sample,1,function(r) which(r == 1))

## Benchmarking
microbenchmark("llmml_int" = llmnl_int(beta = c(4,2,1), Obs = mnl_sample, n_cat = 4),
               "llmml_int_C" = llmnl_int_C(beta = c(4,2,1), Obs = mnl_sample, n_cat = 4),
               times = 100)
## Results
# Unit: microseconds
#         expr     min       lq     mean   median       uq     max neval
#    llmnl_int  76.809  78.6615  81.9677  79.7485  82.8495 124.295   100
#  llmnl_int_C 155.405 157.7790 161.7677 159.2200 161.5805 201.655   100

3) এখন তাদের মধ্যে কল optim:

## Benchmarking with optim
microbenchmark("llmnl_int" = optim(c(4,2,1), llmnl_int, Obs = mnl_sample, n_cat = 4, method = "BFGS", hessian = T, control = list(fnscale = -1)),
               "llmnl_int_C" = optim(c(4,2,1), llmnl_int_C, Obs = mnl_sample, n_cat = 4, method = "BFGS", hessian = T, control = list(fnscale = -1)),
               times = 100)
## Results
# Unit: milliseconds
#         expr      min       lq     mean   median       uq      max neval
#    llmnl_int 12.49163 13.26338 15.74517 14.12413 18.35461 26.58235   100
#  llmnl_int_C 25.57419 25.97413 28.05984 26.34231 30.44012 37.13442   100

আমি কিছুটা অবাক হয়েছি যে আর্টে ভেক্টরাইজড বাস্তবায়ন দ্রুত ছিল। আরসিপ্পিতে আরও দক্ষ সংস্করণটি প্রয়োগ করে (বলুন, আরসিপিআরমাডিলো দিয়ে?) কোনও লাভ হতে পারে? Cc+ অপটিমাইজার ব্যবহার করে আরসিপ্পিতে সমস্ত কিছু পুনরুদ্ধার করা কি আরও ভাল ধারণা?

পিএস: স্ট্যাকওভারফ্লোতে প্রথমবারের মতো পোস্টিং!

উত্তর:


9

সাধারণভাবে যদি আপনি ভেক্টরাইজড ফাংশনগুলি ব্যবহার করতে সক্ষম হন তবে আপনি এটি সরাসরি (আরসিপিপি) আপনার কোডটি সরাসরি চালানোর চেয়ে দ্রুত হিসাবে দেখতে পাবেন। এটি কারণ R তে অনেকগুলি ভেক্টরাইজড ফাংশন (বেস আরে প্রায় সমস্ত ভেক্টরাইজড ফাংশন) সি, সিপিসি বা ফোর্টরান এ লেখা থাকে এবং এর ফলে প্রায়শই লাভ হয় না।

এটি বলেছিল, আপনার Rএবং Rcppকোড উভয় অর্জনের জন্য উন্নতি রয়েছে । অনুকূলতার সাথে কোডটি সাবধানে অধ্যয়ন করা এবং অপ্রয়োজনীয় পদক্ষেপগুলি (মেমোরি অ্যাসাইনমেন্ট, অঙ্কগুলি ইত্যাদি) অপসারণ থেকে আসে।

Rcppকোড অপ্টিমাইজেশন দিয়ে শুরু করা যাক ।

আপনার ক্ষেত্রে মূল অপ্টিমাইজেশন হ'ল অপ্রয়োজনীয় ম্যাট্রিক্স এবং ভেক্টর গণনা অপসারণ করা। কোডটি সংক্ষেপে

  1. শিফট বিটা
  2. এক্সপ (যোগফলের বিটা) এর যোগফলের লগ গণনা করুন [লগ-সম-এক্সপ্রেস]
  3. স্থানান্তরিত বিটা এবং সমস্ত সম্ভাবনার সমষ্টিগুলির জন্য সূচক হিসাবে ওবস ব্যবহার করুন
  4. লগ-সম-এক্সপ্রেটকে বিয়োগ করুন

এই পর্যবেক্ষণটি ব্যবহার করে আমরা আপনার কোডটি 2 টি লুপগুলিতে কমাতে পারি। নোট করুন যে sumকেবল অন্য লুপ (আরও বা কম for(i = 0; i < max; i++){ sum += x }:) তাই এটির পরিমাণ এড়ানো এগুলি কোডকে আরও গতি দিতে পারে (বেশিরভাগ ক্ষেত্রে এটি অপ্রয়োজনীয় অপ্টিমাইজেশন!) Is এছাড়াও আপনার ইনপুটটি Obsএকটি পূর্ণসংখ্যা ভেক্টর, এবং উপাদানগুলিকে মানগুলিতে IntegerVectorকাস্ট করা এড়াতে আমরা টাইপটি ব্যবহার করে কোডটি আরও অনুকূল করতে পারি (রাল্ফ স্টুবনারের উত্তরে ক্রেডিট)।doubleinteger

cppFunction('double llmnl_int_C_v2(NumericVector beta, IntegerVector Obs, int n_cat)
 {

    int n_Obs = Obs.size();

    NumericVector betas = (beta.size()+1);
    //1: shift beta
    for (int i = 1; i < n_cat; i++) {
        betas[i] = beta[i-1];
    };
    //2: Calculate log sum only once:
    double expBetas_log_sum = log(sum(exp(betas)));
    // pre allocate sum
    double ll_sum = 0;

    //3: Use n_Obs, to avoid calling Xby.size() every time 
    for (int i = 0; i < n_Obs; i++) {
        ll_sum += betas(Obs[i] - 1.0) ;
    };
    //4: Use that we know denom is the same for all I:
    ll_sum = ll_sum - expBetas_log_sum * n_Obs;
    return ll_sum;
}')

নোট করুন যে আমি বেশ কয়েকটি মেমোরি বরাদ্দ সরিয়েছি এবং ফর-লুপে অপ্রয়োজনীয় গণনাগুলি সরিয়েছি। এছাড়াও আমি ব্যবহার করেছি যা denomসমস্ত পুনরাবৃত্তির জন্য একই এবং চূড়ান্ত ফলাফলের জন্য কেবল গুণিত।

আমরা আপনার আর-কোডে অনুরূপ অপটিমাইজেশন করতে পারি, যার ফলাফল নীচের ফাংশন:

llmnl_int_R_v2 <- function(beta, Obs, n_cat) {
    n_Obs <- length(Obs)
    betas <- c(0, beta)
    #note: denom = log(sum(exp(betas)))
    sum(betas[Obs]) - log(sum(exp(betas))) * n_Obs
}

নোট করুন যে ফাংশনটির জটিলতা একেবারে হ্রাস করা হয়েছে যা অন্যদের পড়ার পক্ষে সহজ করে তোলে। আমি নিশ্চিত হয়েছি যে আমি কোথাও কোডটিতে গণ্ডগোল করি নি সেগুলি পরীক্ষা করে দেখি যে তারা একই ফলাফল দেয়:

set.seed(2020)
mnl_sample <- t(rmultinom(n = 1000,size = 1,prob = c(0.3, 0.4, 0.2, 0.1)))
mnl_sample <- apply(mnl_sample,1,function(r) which(r == 1))

beta = c(4,2,1)
Obs = mnl_sample 
n_cat = 4
xr <- llmnl_int(beta = beta, Obs = mnl_sample, n_cat = n_cat)
xr2 <- llmnl_int_R_v2(beta = beta, Obs = mnl_sample, n_cat = n_cat)
xc <- llmnl_int_C(beta = beta, Obs = mnl_sample, n_cat = n_cat)
xc2 <- llmnl_int_C_v2(beta = beta, Obs = mnl_sample, n_cat = n_cat)
all.equal(c(xr, xr2), c(xc, xc2))
TRUE

ভাল যে একটি স্বস্তি।

কর্মক্ষমতা:

পারফরম্যান্সটি চিত্রিত করার জন্য আমি মাইক্রোবেঞ্চমার্ক ব্যবহার করব। অনুকূলিত ফাংশনগুলি দ্রুত, তাই 1e5আবর্জনা সংগ্রাহকের প্রভাব হ্রাস করার জন্য আমি ফাংশনগুলি বার করে চালাব

microbenchmark("llmml_int_R" = llmnl_int(beta = beta, Obs = mnl_sample, n_cat = n_cat),
               "llmml_int_C" = llmnl_int_C(beta = beta, Obs = mnl_sample, n_cat = n_cat),
               "llmnl_int_R_v2" = llmnl_int_R_v2(beta = beta, Obs = mnl_sample, n_cat = n_cat),
               "llmml_int_C_v2" = llmnl_int_C_v2(beta = beta, Obs = mnl_sample, n_cat = n_cat),
               times = 1e5)
#Output:
#Unit: microseconds
#           expr     min      lq       mean  median      uq        max neval
#    llmml_int_R 202.701 206.801 288.219673 227.601 334.301  57368.902 1e+05
#    llmml_int_C 250.101 252.802 342.190342 272.001 399.251 112459.601 1e+05
# llmnl_int_R_v2   4.800   5.601   8.930027   6.401   9.702   5232.001 1e+05
# llmml_int_C_v2   5.100   5.801   8.834646   6.700  10.101   7154.901 1e+05

এখানে আমরা আগের মতো একই ফলাফল দেখতে পাচ্ছি। এখন নতুন ফাংশনগুলি তাদের প্রথম পাল্টা অংশগুলির তুলনায় প্রায় 35x দ্রুত (আর) এবং 40x দ্রুত (সিপিপি)। মজার বিষয় হল যথেষ্ট পরিমাণে অনুকূলিত Rফাংশনটি এখনও আমার অপ্টিমাইজড Cppফাংশনের চেয়ে খুব সামান্য (0.3 মিমি বা 4%) দ্রুত । আমার সেরা বাজিটি হ'ল Rcppপ্যাকেজ থেকে কিছু ওভারহেড রয়েছে এবং এটি অপসারণ করা হলে দুটিই অভিন্ন বা আর।

একইভাবে আমরা অপ্টিম ব্যবহার করে পারফরম্যান্স চেক করতে পারি।

microbenchmark("llmnl_int" = optim(beta, llmnl_int, Obs = mnl_sample, 
                                   n_cat = n_cat, method = "BFGS", hessian = F, 
                                   control = list(fnscale = -1)),
               "llmnl_int_C" = optim(beta, llmnl_int_C, Obs = mnl_sample, 
                                     n_cat = n_cat, method = "BFGS", hessian = F, 
                                     control = list(fnscale = -1)),
               "llmnl_int_R_v2" = optim(beta, llmnl_int_R_v2, Obs = mnl_sample, 
                                     n_cat = n_cat, method = "BFGS", hessian = F, 
                                     control = list(fnscale = -1)),
               "llmnl_int_C_v2" = optim(beta, llmnl_int_C_v2, Obs = mnl_sample, 
                                     n_cat = n_cat, method = "BFGS", hessian = F, 
                                     control = list(fnscale = -1)),
               times = 1e3)
#Output:
#Unit: microseconds
#           expr       min        lq      mean    median         uq      max neval
#      llmnl_int 29541.301 53156.801 70304.446 76753.851  83528.101 196415.5  1000
#    llmnl_int_C 36879.501 59981.901 83134.218 92419.551 100208.451 190099.1  1000
# llmnl_int_R_v2   667.802  1253.452  1962.875  1585.101   1984.151  22718.3  1000
# llmnl_int_C_v2   704.401  1248.200  1983.247  1671.151   2033.401  11540.3  1000

আবারও ফলাফল একই রকম।

উপসংহার:

সংক্ষিপ্ত উপসংহার হিসাবে এটি লক্ষণীয় যে এটি একটি উদাহরণ, যেখানে আপনার কোডটি আরসিপিতে রূপান্তর করা আসলেই সমস্যার পক্ষে উপযুক্ত নয়। এটি সর্বদা ক্ষেত্রে হয় না, তবে আপনার কোডের এমন কোনও অঞ্চল রয়েছে যেখানে অপ্রয়োজনীয় গণনা সম্পাদন করা হয় তা দেখার জন্য এটি প্রায়শই আপনার ক্রিয়াকলাপের দিকে একবার নজর দেওয়া। বিশেষত এমন পরিস্থিতিতে যেখানে কেউ বিল্টিন ভেক্টরাইজড ফাংশন ব্যবহার করে, কোডটি আরসিপিতে রূপান্তর করা প্রায়শই সময় মূল্য নয়। for-loopsফোর-লুপটি সরিয়ে ফেলার জন্য যদি কেউ এমন কোড ব্যবহার করে যা সহজেই ভেক্টরাইজ করা যায় না তবে দুর্দান্ত পরিবর্তনগুলি দেখতে পাওয়া যায়।


1
আপনি কিছু কাস্টকে অপসারণ Obsহিসাবে বিবেচনা করতে পারেন IntegerVector
রাল্ফ স্টুবনার

আপনার উত্তরে এটি লক্ষ্য করার জন্য আপনাকে ধন্যবাদ দেওয়ার আগে এটি কেবল সংযুক্ত ছিল। এটা কেবল আমার পাশ দিয়ে গেল আমি আমার উত্তর @ র‌্যালফস্টাবনারে আপনাকে এর জন্য ক্রেডিট দিয়েছি। :-)
অলিভার

2
আপনি যেমন খেলনার উদাহরণটিতে লক্ষ্য করেছেন (ইন্টারসেপ্ট-কেবলমাত্র এমএনএল মডেল) লিনিয়ার প্রেডিক্টর ( beta) পর্যবেক্ষণের উপর স্থির থাকে Obs। যদি আমাদের সময় পরিবর্তিত হয় ভবিষ্যদ্বাণীকারীগুলির denomজন্য Obsডিজাইনের ম্যাট্রিক্সের মানের উপর নির্ভর করে প্রত্যেকের জন্য একটি অন্তর্নিহিত গণনা প্রয়োজনীয় হয়ে উঠত X। বলা হচ্ছে, আমি ইতিমধ্যে কিছু চমৎকার লাভ সহ আমার বাকী কোডগুলিতে আপনার পরামর্শগুলি ইতিমধ্যে বাস্তবায়ন করছি :)। আপনার খুব অন্তর্দৃষ্টিপূর্ণ জবাবের জন্য আপনাকে @ রালফস্টাবনার, @ অলিভার এবং @ থ্যাঙ্ক ধন্যবাদ! এখন আমার পরের বাধা!
স্মাইলাইনার

1
আমি আনন্দিত যে আমরা সাহায্য করতে পারি। সর্বাধিক সাধারণ ক্ষেত্রে দ্বিতীয়টির প্রতিটি ধাপে ডেনমকে বিয়োগ করা গণনা করা for-loopযা আপনাকে সর্বাধিক উপকার দেবে। আরও সাধারণ ক্ষেত্রে আমি আপনাকে model.matrix(...)আপনার ফাংশনগুলিতে ইনপুট দেওয়ার জন্য ম্যাট্রিক্স তৈরি করতে ব্যবহার করার পরামর্শ দিচ্ছি ।
অলিভার

9

আপনার সি ++ ফাংশনটি নিম্নলিখিত পর্যবেক্ষণগুলি ব্যবহার করে দ্রুত তৈরি করা যেতে পারে। কমপক্ষে প্রথমটি আপনার আর ফাংশনের সাথেও ব্যবহৃত হতে পারে:

  • আপনি যেভাবে গণনা denom[i]করছেন তা প্রত্যেকের জন্য একই i। অতএব এটি double denomএকবার ব্যবহার করার এবং এই গণনাটি করার জন্য বোধগম্য । আমি শেষ পর্যন্ত এই সাধারণ শব্দটি বিয়োগ করে ফ্যাক্টরও করি।

  • আপনার পর্যবেক্ষণগুলি আসলে আর পাশের একটি পূর্ণসংখ্যার ভেক্টর এবং আপনি সেগুলি সি ++ তেও পূর্ণসংখ্যা হিসাবে ব্যবহার করছেন। একটি IntegerVectorদিয়ে শুরু করে প্রচুর কাস্টিং অপ্রয়োজনীয় করে তোলে।

  • আপনি সি ++ তেও একটি NumericVectorব্যবহার করে সূচক করতে পারেন IntegerVector। এটি পারফরম্যান্সে সহায়তা করে কিনা আমি নিশ্চিত নই, তবে কোডটি কিছুটা খাটো করে তুলেছে।

  • পারফরম্যান্সের চেয়ে আরও কিছু পরিবর্তন যা স্টাইলের সাথে সম্পর্কিত।

ফলাফল:

double llmnl_int_C(NumericVector beta, IntegerVector Obs, int n_cat) {

    int n_Obs = Obs.size();

    NumericVector betas(beta.size()+1);
    for (int i = 1; i < n_cat; ++i) {
        betas[i] = beta[i-1];
    };

    double denom = log(sum(exp(betas)));
    NumericVector Xby = betas[Obs - 1];

    return sum(Xby) - n_Obs * denom;
}

আমার জন্য এই ফাংশনটি আপনার আর ফাংশন থেকে প্রায় দশগুণ দ্রুত।


আপনার উত্তর রাল্ফ জন্য ধন্যবাদ, ইনপুট টাইপ না। আমি এটিকে আমার উত্তরে অন্তর্ভুক্ত করেছি পাশাপাশি আপনাকে কৃতিত্বও দিয়েছি। :-)
অলিভার

7

রাল্ফ এবং অলিভারের উত্তরগুলির চেয়ে আমি চারটি সম্ভাব্য অপটিমাইজেশন সম্পর্কে ভাবতে পারি ।

(আপনার উত্তরগুলি গ্রহণ করা উচিত, তবে আমি কেবল আমার 2 সেন্ট যোগ করতে চেয়েছিলাম)।

1) // [[Rcpp::export(rng = false)]]পৃথক সি ++ ফাইলে ফাংশনটিতে একটি মন্তব্য শিরোনাম হিসাবে ব্যবহার করুন। এটি আমার মেশিনে ~ 80% গতি বাড়ায়। (এটি 4 এর মধ্যে সর্বাধিক গুরুত্বপূর্ণ পরামর্শ)।

2) cmathসম্ভব হলে পছন্দ করুন । (এই ক্ষেত্রে, এটি কোনও পার্থক্য বলে মনে হচ্ছে না)।

3) যখনই সম্ভব বরাদ্দ এড়িয়ে চলুন, উদাহরণস্বরূপ betaকোনও নতুন ভেক্টরে স্থানান্তর করবেন না ।

৪) প্রসারিত লক্ষ্য: SEXPআরসিপিপি ভেক্টরগুলির চেয়ে পরামিতিগুলি ব্যবহার করুন । (পাঠকের অনুশীলন হিসাবে বামে)। আরসিপিপি ভেক্টরগুলি খুব পাতলা র‌্যাপার, তবে তারা এখনও মোড়ক এবং সেখানে একটি ছোট ওভারহেড রয়েছে।

এই পরামর্শগুলি গুরুত্বপূর্ণ হবে না, যদি না আপনি এই ফাংশনটিকে একটি শক্ত লুপে কল করছেন optim। সুতরাং যে কোনও ওভারহেড অত্যন্ত গুরুত্বপূর্ণ।

এজলাস:

microbenchmark("llmnl_int_R_v1" = optim(beta, llmnl_int, Obs = mnl_sample, 
                                      n_cat = n_cat, method = "BFGS", hessian = F, 
                                      control = list(fnscale = -1)),
             "llmnl_int_R_v2" = optim(beta, llmnl_int_R_v2, Obs = mnl_sample, 
                                      n_cat = n_cat, method = "BFGS", hessian = F, 
                                      control = list(fnscale = -1)),
             "llmnl_int_C_v2" = optim(beta, llmnl_int_C_v2, Obs = mnl_sample, 
                                      n_cat = n_cat, method = "BFGS", hessian = F, 
                                      control = list(fnscale = -1)),
             "llmnl_int_C_v3" = optim(beta, llmnl_int_C_v3, Obs = mnl_sample, 
                                      n_cat = n_cat, method = "BFGS", hessian = F, 
                                      control = list(fnscale = -1)),
             "llmnl_int_C_v4" = optim(beta, llmnl_int_C_v4, Obs = mnl_sample, 
                                      n_cat = n_cat, method = "BFGS", hessian = F, 
                                      control = list(fnscale = -1)),
             times = 1000)


Unit: microseconds
expr      min         lq       mean     median         uq        max neval cld
llmnl_int_R_v1 9480.780 10662.3530 14126.6399 11359.8460 18505.6280 146823.430  1000   c
llmnl_int_R_v2  697.276   735.7735  1015.8217   768.5735   810.6235  11095.924  1000  b 
llmnl_int_C_v2  997.828  1021.4720  1106.0968  1031.7905  1078.2835  11222.803  1000  b 
llmnl_int_C_v3  284.519   295.7825   328.5890   304.0325   328.2015   9647.417  1000 a  
llmnl_int_C_v4  245.650   256.9760   283.9071   266.3985   299.2090   1156.448  1000 a 

v3 এর সাথে অলিভারের উত্তর rng=false। ভি 4 টি পরামর্শ # 2 এবং # 3 অন্তর্ভুক্ত রয়েছে।

কাজ:

#include <Rcpp.h>
#include <cmath>
using namespace Rcpp;

// [[Rcpp::export(rng = false)]]
double llmnl_int_C_v4(NumericVector beta, IntegerVector Obs, int n_cat) {

  int n_Obs = Obs.size();
  //2: Calculate log sum only once:
  // double expBetas_log_sum = log(sum(exp(betas)));
  double expBetas_log_sum = 1.0; // std::exp(0)
  for (int i = 1; i < n_cat; i++) {
    expBetas_log_sum += std::exp(beta[i-1]);
  };
  expBetas_log_sum = std::log(expBetas_log_sum);

  double ll_sum = 0;
  //3: Use n_Obs, to avoid calling Xby.size() every time 
  for (int i = 0; i < n_Obs; i++) {
    if(Obs[i] == 1L) continue;
    ll_sum += beta[Obs[i]-2L];
  };
  //4: Use that we know denom is the same for all I:
  ll_sum = ll_sum - expBetas_log_sum * n_Obs;
  return ll_sum;
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.