Instructions
- Import Review.csv data first
- Run the provided code block (visible below) to prepare data and
train a baseline model then answer the questions.
- Do not change the given R code.
Baseline Code (Run This First)
# Read Review.csv first.
# Review <- read.csv("data/Review.csv", header=FALSE)
# ============================================================
# Keras 3 (R: keras3) sentiment model for Korean reviews
# ============================================================
library(keras3)
set.seed(41)
review = Review
colnames(review) <- c("score", "comment")
review$score <- as.integer(review$score)
review <- na.omit(review) # Remove NAs
review$score = ifelse(review$score <= 5, 0, 1) # Recode to 0-1 data
# --------------------------
# Train / valid / test split
# --------------------------
n <- nrow(review) %/% 10
idx <- sample.int(n)
review <- review[idx, ]
n_test <- max(1, round(0.20 * n))
test_set <- review[seq_len(n_test), ]
train_set <- review[-seq_len(n_test), ]
x_train <- train_set$comment
x_test <- test_set$comment
y_train <- train_set$score
y_test <- test_set$score
# ------------------------------------------------
# Text vectorization (minimal preprocessing)
# ------------------------------------------------
# - Keeps text as-is (no lowercasing/stripping) because it's Korean.
max_words <- 20000L
maxlen <- 50L # you can raise to reduce truncation
vec <- layer_text_vectorization(
max_tokens = max_words,
output_mode = "int",
output_sequence_length = maxlen,
standardize = NULL # keep Korean punctuation/spacing as-is
)
# Build the vocabulary from training text only
adapt(vec, train_set$comment)
# Save the whole vocabulary (not necessary)
vocab <- keras3::get_vocabulary(vec); head(vocab, 30)
# ------------------------------------------------
# Model: TextVectorization -> Embedding -> CNN
# ------------------------------------------------
embed_dim <- 128L
model <- keras3::keras_model_sequential(
layers = list(
vec,
keras3::layer_embedding(input_dim = max_words, output_dim = embed_dim, mask_zero = FALSE),
keras3::layer_spatial_dropout_1d(rate = 0.2),
keras3::layer_conv_1d(filters = 128, kernel_size = 5, activation = "relu", padding = "same"),
keras3::layer_conv_1d(filters = 128, kernel_size = 5, activation = "relu", padding = "same"),
keras3::layer_global_max_pooling_1d(),
keras3::layer_dropout(rate = 0.4),
keras3::layer_dense(units = 64, activation = "relu"),
keras3::layer_dropout(rate = 0.3),
keras3::layer_dense(units = 1, activation = "sigmoid")
)
)
model |> compile(
optimizer = "adam",
loss = "binary_crossentropy",
metrics = "accuracy"
)
# ------------------------------------------------
# Train
# ------------------------------------------------
history <- model |>
fit(
x = x_train, y = y_train,
epochs = 5,
batch_size = 64,
validation_split = 0.2,
verbose = 2
)
Questions
1) Print a readable model summary. How many
parameters are there in total?
summary(model)
## Model: "sequential"
## ┌───────────────────────────────────┬──────────────────────────┬───────────────
## │ Layer (type) │ Output Shape │ Param #
## ├───────────────────────────────────┼──────────────────────────┼───────────────
## │ text_vectorization │ (None, 50) │ 0
## │ (TextVectorization) │ │
## ├───────────────────────────────────┼──────────────────────────┼───────────────
## │ embedding (Embedding) │ (None, 50, 128) │ 2,560,000
## ├───────────────────────────────────┼──────────────────────────┼───────────────
## │ spatial_dropout1d │ (None, 50, 128) │ 0
## │ (SpatialDropout1D) │ │
## ├───────────────────────────────────┼──────────────────────────┼───────────────
## │ conv1d (Conv1D) │ (None, 50, 128) │ 82,048
## ├───────────────────────────────────┼──────────────────────────┼───────────────
## │ conv1d_1 (Conv1D) │ (None, 50, 128) │ 82,048
## ├───────────────────────────────────┼──────────────────────────┼───────────────
## │ global_max_pooling1d │ (None, 128) │ 0
## │ (GlobalMaxPooling1D) │ │
## ├───────────────────────────────────┼──────────────────────────┼───────────────
## │ dropout (Dropout) │ (None, 128) │ 0
## ├───────────────────────────────────┼──────────────────────────┼───────────────
## │ dense (Dense) │ (None, 64) │ 8,256
## ├───────────────────────────────────┼──────────────────────────┼───────────────
## │ dropout_1 (Dropout) │ (None, 64) │ 0
## ├───────────────────────────────────┼──────────────────────────┼───────────────
## │ dense_1 (Dense) │ (None, 1) │ 65
## └───────────────────────────────────┴──────────────────────────┴───────────────
## Total params: 8,197,253 (31.27 MB)
## Trainable params: 2,732,417 (10.42 MB)
## Non-trainable params: 0 (0.00 B)
## Optimizer params: 5,464,836 (20.85 MB)
2) Print the accuracy using test dataset.
model |> evaluate(x_test, y_test)
## 59/59 - 0s - 2ms/step - accuracy: 0.8143 - loss: 0.8002
## $accuracy
## [1] 0.8143392
##
## $loss
## [1] 0.8001646
3) Compute predicted probabilities, convert to
class labels with threshold 0.5, and report the confusion
matrix.
pred_prob <- model |> predict(x_test)
## 59/59 - 0s - 4ms/step
pred_lab <- as.integer(pred_prob > 0.5)
print(table(Predicted = pred_lab, Actual = y_test))
## Actual
## Predicted 0 1
## 0 157 139
## 1 208 1365
4) Print the labels, predicted probabilities, and raw text for the
three observations predicted to be the most interesting.
Perform the same thing for the three observations predicted to
be the least interesting.
eg_idx <- tail(order(pred_prob), 3)
for (i in eg_idx) {
cat("\n--- Example ---------------------------\n")
cat("True:", y_test[i], "\n")
p <- as.numeric(model |> predict(x_test[i], verbose = 0))
cat(sprintf("Pred prob (positive=1): %.3f\n", p))
cat("Text:", x_test[i], "\n")
}
##
## --- Example ---------------------------
## True: 1
## Pred prob (positive=1): 1.000
## Text: 가장최근에 본 영화에요. 별 기대하지 않고 보았는데 유쾌하고 재밌었습니다.
##
## --- Example ---------------------------
## True: 1
## Pred prob (positive=1): 1.000
## Text: 어우 !! 재밌어요 ㅋㅋ
##
## --- Example ---------------------------
## True: 1
## Pred prob (positive=1): 1.000
## Text: 진짜 재밌었어요 근데 의외로 평점이 낮네요..
eg_idx <- head(order(pred_prob), 3)
for (i in eg_idx) {
cat("\n--- Example ---------------------------\n")
cat("True:", y_test[i], "\n")
p <- as.numeric(model |> predict(x_test[i], verbose = 0))
cat(sprintf("Pred prob (positive=1): %.3f\n", p))
cat("Text:", x_test[i], "\n")
}
##
## --- Example ---------------------------
## True: 0
## Pred prob (positive=1): 0.000
## Text: 돈이아까워 돈이아까워 돈이아까워 돈이아까워 돈이아까워 돈이아까워 돈이아까워 돈이아까워 돈이아까워 돈이아까워 돈이아까워 돈이아까워 돈이아까워 돈이아까워 돈이아까워 돈이아까워 돈이아까워 돈이아까워 돈
##
## --- Example ---------------------------
## True: 0
## Pred prob (positive=1): 0.000
## Text: 스토리가 들어맞는게 하나도 없네. 재미도 없고 감동도 없고 액션도 없고 스토리는 말도 안되고 장면은 더럽게 지루하고
##
## --- Example ---------------------------
## True: 0
## Pred prob (positive=1): 0.000
## Text: 유하라는 이름땜에 5점 준다. 지겹고 유치한 영화다.