아주 간단한 Deep Learning에 대한 기초를 이전 글들을 통해 쌓아봤습니다. 이제 본격적으로 NN(Neural Network)를 시작할 겁니다. (기대되네요 >_<)


먼저 간단한 NN으로 다층 퍼셉트론을 이용한 XOR 문제를 해결해볼 것입니다. XOR(eXclusive OR: 베타적 논리합)은 논리 연산 중 하나입니다. 컴퓨터 프로그래밍에서는 A^B로 나타낼 수 있습니다. 두 변수가 있고, 이를 XOR 했을 때 결과는 다음 표와 같습니다.


B

A^B

 False

False

False

True

False

True

False

True

True

True

True

False


OR 연산과 다른 점은 A와 B를 연산했을 때 True가 아니라 False가 나온다는 점입니다. True를 1, False를 0이라고 두고 결과값 True를 파란색 점, False를 빨간색 점이라고 두면 다음과 같은 그래프로 나타낼 수 있습니다.

보시다시피, 이렇게 나와있는 형태의 경우 직선으로는 두 종류를 정확도 100%로 분할할 수 없습니다. 따라서 선형 분류가 불가능하다는 소리인데, 이런 경우가 XOR 말고도 꽤 있습니다. 따라서 이런 문제에서는 sigmoid 함수(뉴런)를 한 번만 사용하는 것이 아닌 여러 번을 겹쳐 사용하여 문제를 해결합니다. 간단히 아래와 같이 NN을 설계해보겠습니다.

(그래프 표기는 http://www.asimovinstitute.org/neural-network-zoo/를 따랐습니다. 넘나 이뻐서... 앞으로도 그래프는 이것을 따를 예정입니다)

노란색 노드: 입력, 초록색 노드: 은닉층, 주황색 노드: 출력

노란색 노드를 제외하고는 모두 sigmoid를 가지고 있는 연산노드입니다. 각 노드를 왼쪽부터 순서대로 Layer 1(노란색), Layer 2(초록색), Layer 3(주황색)이라고 하고 각 층마다 행렬로 묶어서 연산을 하면 좀 더 간단하게 연산을 나타낼 수 있습니다. 아래가 그 모습입니다.


이 상태에서 Layer 2, Layer 3에 sigmoid를 적용하면 끝인거죠. 참 쉽죠?


다음으로 이 네트워크를 어떻게 최적화시킬지 알아보겠습니다. 물론 TensorFlow 상에선 cross-entropy를 적용하고 GradientDescentOptimizer를 적용하면 바로 해결됩니다. 그래도 간단히 최적화시키는 기법인 Backpropagation을 알아가면 좋을 거 같아서 한 번 알아보겠습니다.


Backpropagation(역전파법)은 다음과 같은 과정으로 이뤄집니다.

1. Forward Propagation: 임의의 작은 수로 Network의 Weight를 초기화합니다. 그리고 학습 데이터를 입력하여 출력을 만들어냅니다.

2. label과 출력의 에러를 계산해냅니다. 그리고 이를 기반으로 Weight에 대한 미분값을 구합니다.


3. 계산된 에러의 미분값을 이용해 학습률(Learning rate)를 정하고 학습률만큼 수정된 Weight를 구해 업데이트합니다. 그리고 네트워크가 일정한 정확도

     에 도달할 때까지 이 과정을 반복합니다.


수식으로 나타내보고 싶지만, 이게 여간 쉬워보이진 않아서(...) 나중에 기회가 되면 수식으로 정리해보겠습니다.


이렇게 기초적인 NN에 대해 정리를 해봤습니다. 아래 코드를 통해서 이를 직접 테스트할 수 있습니다. Jupyter Notebook 파일도 첨부합니다.

Hello, NN!.ipynb

import tensorflow as tf
import numpy as np

x = [[0, 0], [0, 1], [1, 0], [1 ,1]]
y = [[0], [1], [1], [0]]

X = tf.placeholder(tf.float32, [None, 2], name="X-Input")
Y = tf.placeholder(tf.float32, [None, 1], name="Y-Input")

W1 = tf.Variable(tf.random_uniform([2, 2], -1., 1.), name="W1")
W2 = tf.Variable(tf.random_uniform([2, 1], -1., 1.), name="W2")
B1 = tf.Variable(tf.zeros([2]), name="B1")
B2 = tf.Variable(tf.zeros([1]), name="B2")

with tf.name_scope("Layer2") as scope:
    L2 = tf.sigmoid(tf.matmul(X, W1) + B1)
with tf.name_scope("Layer3") as scope:
    L3 = tf.sigmoid(tf.matmul(L2, W2) + B2)

with tf.name_scope("Cost") as scope:
    cost = -tf.reduce_mean(Y * tf.log(L3) + (1 - Y) * tf.log(1 - L3))
    cost_summ = tf.summary.scalar("Cost", cost)    

with tf.name_scope("Train") as scope:
    train = tf.train.GradientDescentOptimizer(0.5).minimize(cost)

with tf.name_scope("Accuracy") as scope:
    accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.floor(L3 + 0.5), Y), tf.float32))
    accuracy_summ = tf.summary.scalar("Accuracy", accuracy)

init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)

merged = tf.summary.merge_all()
writer = tf.summary.FileWriter("./logs/", sess.graph)

for step in xrange(2001):
    summary, _ = sess.run([merged, train], feed_dict={X: x, Y: y})
    writer.add_summary(summary, step)
    if step % 500 == 0:
        print sess.run(cost, feed_dict={X: x, Y: y})

print sess.run(accuracy, feed_dict={X: x, Y: y})


추가적으로 설명하자면, Deep Learning에서 Accuracy 등의 변화를 좀 더 편하게 살피고, 네트워크의 구조를 살피기 좋게 TensorFlow에서는 TensorBoard라는 것을 제공합니다. 따라서 앞으로 다루는 NN에 대해서는 TensorBoard를 적용해서 편리하게 네트워크를 보려고 합니다. 그래서 이번 기회에 이 TensorBoard를 소개하고자 합니다. TensorBoard 사용방법은 다음과 같습니다.


1. 그래프에 표기하고 싶은 변수들의 이름을 정한다

X = tf.placeholder(tf.float32, [None, 2], name="X-Input")


2. 기록하고 싶은 부분(Accuracy, Cost, Layer 등)을 scop를 통해 묶는다.

with tf.name_scope("Train") as scope:


3. Scalar 그래프 또는 Histogram 그래프로 보고 싶은 변수는 아래와 같이 이름과 함께 등록해둔다.

cost_summ = tf.summary.scalar("Cost", cost)


4. 모든 Summaries를 Merge하고, 이 Summaries를 기록할 Writer를 만든다. 이때 저장될 log의 경로를 정합니다.

merged = tf.summary.merge_all()

writer = tf.summary.FileWriter("./logs/", sess.graph)


5. merged된 것을 돌리고 매 step마다 writer에 추가합니다.

summary, _ = sess.run([merged, train], feed_dict={X: x, Y: y})

writer.add_summary(summary, step)


6. TensorBoard를 이용해 기록된 log, 네트워크의 graph를 확인합니다. 이때 경로 상에 ','가 있는 경우 그걸 기점으로 경로가 나눠져버리니 이 점 고려하셔야 합니다. (의도는 여러 개의 log를 볼 수 있게 ','를 통해 여러 경로를 받으려는 거 같은데, ""로 경로를 하나 감싸도 이러는 거 보니 버그 맞는 듯) 만약 로그가 정상적으로 로딩이 안 되면, --debug를 붙여서 TensorBoard의 상태를 확인해보시면 됩니다.

tensorboard --logdir=./logs/


아래는 위 코드를 실행시키고 TensorBoard로 Cost, Accuracy, NN의 Graph를 확인한 내용입니다.



이렇게 간단한 NN을 끝내봤습니다. 다음엔 ReLU를 할 거 같습니다.


오늘은 머신러닝 데이터셋 중에서 유명한 붓꽃(iris) 데이터셋을 이용하여 여러 개의 sigmoid 함수를 써서 다변수의 classification을 해보겠습니다.
데이터셋은 다음의 링크(http://ai-times.tistory.com/418)에서 다운받으실 수 있습니다.


일단 먼저 붓꽃 데이터셋은 4개의 항목(SepalLength, SepalWidth, PetalLength, PetalWidth)과 그에 맞는 붓꽃의 종류(Species)가 있습니다. 이때 각 항목에 맞는 데이터를 집어 넣고 그 항목에 맞는 붓꽃은 무슨 종류인지 알아 내려면 이 항목들이 어떤 특성을 가지고 있는지 확인해야 합니다. 따라서 각 항목별로 그래프를 그려서 분포를 확인해봅니다.

Sepal Width, Sepal Length 그래프에서 versicolor 종과 virginica 종이 섞여서 분포하는 것을 제외하고는 대부분 각 종마다 특정 좌표에 많이 모여있다는 게 보입니다. 이런 경우에는 Softmax Classification을 적용하여 종을 알고 싶은 붓꽃의 항목 데이터를 입력하여 붓꽃의 종을 알아내는 인공지능을 만들어 낼 수 있는 것이죠.


자, 그러면 이제 프로그래밍을 해볼 차례입니다. 우리의 목표는 붓꽃의 항목 데이터를 받아서 붓꽃의 종(3가지)을 알아내는 겁니다. 이때 Logistic Regression은 어떤 특정 항목에 대한 확률을 보여주는 방법이기 때문에 3가지 종에 각각 Logistic Regression을 적용해주면 됩니다. 즉, setosa인지 아닌지 확률을 판단하는 Logistic Regression, versicolor인지 아닌지 확률을 판단하는 Logistic Regression, virginica인지 아닌지 확률을 판단하는 Logistic Regression을 각각 만들어서 각 함수에 항목 데이터를 입력해서 각 종에 대한 확률을 구하고, 그 확률이 가장 높은 게 그 종이 되는 겁니다.


그러나 이렇게 각 종마다 Logistic Regression을 적용하면 output이 모두 같게 (예: (0.9, 0.9, 0.9)) 나올 수도 있고, 성능도 조금 떨어지기 때문에 각 종에 맞을 확률의 합이 1(예: (0.7, 0.2, 0.1))로 나오도록 sigmoid를 확장시켜 Softmax라는 새로운 식이 등장하게 되었습니다. Softmax는 다음과 같습니다.

이때 K=2라면 원래 sigmoid 형태의 함수가 나오게 되죠.



자, 그러면 이제 프로그래밍을 해볼까요? 일단 총 3개의 종이 있고, 4개의 항목이 있으니 이에 대한 3행 4열의 weight 행렬을 만들어야 합니다. 그리고 3행의 bias 행렬도 필요하겠죠.


이때 input은 ?행(데이터 개수) 4열이기 때문에 편의상 W를 Tranpose 해주고 곱한 다음에 b도 Tranpose 하고 더하면 됩니다. 즉, 간단하게 4행 3열의 weight, 3열의 bias 행렬을 생성해서 사용하면 됩니다.


다음으로는 이 W, b를 최적화시켜서 최대한 올바른 Softmax 함수가 나오도록 해 줄 cost값을 도출할 함수가 필요합니다. 이 함수를 Cross Entropy 함수라고 합니다. 이 함수는 다음과 같습니다.


sigmoid에서 Softmax가 나온 것처럼 이것도 sigmoid에서 쓰인 cost함수랑 비슷하게 생겼습니다. TensorFlow에서 위 식을 직접 구현해서 써도 되긴 하지만, TensorFlow Tutorial에서 MNIST for ML Beginners 문서를 보면 불안정하다고 나와있으니 tf.nn.softmax_cross_entropy_with_logits 함수를 사용할 것입니다. (Note that in the source code, we don't use this formulation, because it is numerically unstable. Instead, we apply tf.nn.softmax_cross_entropy_with_logits on the unnormalized logits (e.g., we call softmax_cross_entropy_with_logits on tf.matmul(x, W) + b), because this more numerically stable function internally computes the softmax activation. In your code, consider using tf.nn.(sparse_)softmax_cross_entropy_with_logits instead)


이제 Softmax 알고리즘은 다 준비되었습니다. 위 내용을 기반으로 프로그래밍을 해보면 아래와 같은 코드가 나옵니다. (이번에는 Jupyter Notebook을 이용하여 코딩을 해서 따로 이 파일도 첨부하겠습니다.)


softmax_classification.ipynb

import tensorflow as tf
import pandas as pd
import numpy as np

keys = ['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth']
data = pd.read_csv("iris.csv")
data = data.drop('caseno', axis=1)

species = list(data['Species'].unique())
data['class'] = data['Species'].map(lambda x: np.eye(len(species))[species.index(x)])

testset = data.sample(50)
trainset = data.drop(testset.index)

X = tf.placeholder(tf.float32, [None, 4])
Y = tf.placeholder(tf.float32, [None, 3])

W = tf.Variable(tf.zeros([4, 3]), name="Weight")
b = tf.Variable(tf.zeros([3]), name="Bias")

H = tf.nn.softmax(tf.matmul(X, W) + b)

cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(H, Y, name="Cross_Entropy"))
optimizer = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)

accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.argmax(H, 1), tf.argmax(Y, 1)), tf.float32))

init = tf.initialize_all_variables()

sess = tf.Session()
sess.run(init)

trainset_class = [y for y in trainset['class'].values]

for step in xrange(1001):
    sess.run(optimizer, feed_dict={X: trainset[keys].values,
                                   Y: trainset_class})
    if step % 100 == 0:
        print step, sess.run(accuracy, feed_dict={X: trainset[keys].values,
                                                  Y: trainset_class})
        print sess.run(cross_entropy, feed_dict={X: trainset[keys].values,
                                                 Y: trainset_class})
# Check which data is error
# trainset_result = sess.run(H, feed_dict={X: trainset[keys].values})

# error_data = []

# for x in xrange(trainset.shape[0]):
#     if np.argmax(trainset_result[x], 0) != np.argmax(trainset_class[x], 0):
#         error_data.append(trainset.values[x])
        
# print error_data

# Check testdata's accuracy
print sess.run(accuracy, feed_dict={X: testset[keys].values,
                                    Y: [y for y in testset['class'].values]})

# Test which species accords this data
species = ["setosa", "versicolor", "virginica"]

sample = data.sample(1)
result = species[np.argmax(sess.run(H, feed_dict={X: sample[keys].values}))]
print "The flower which has", sample[keys].values, "may be", result
if result == sample['Species'].values:
    print "Correct!"
else:
    print "Incorrect!"

실행시켜보면 거의 평균적으로 정확도가 98% 정도 나옵니다.


이렇게 Softmax Classification을 끝내봤습니다. 다음에는 TensorBoard를 소개하면서 NN을 시작하지 않을까 싶네요.


본 글은 필자가 Ubuntu 16.04.1 64bit 기반의 Unity와 Gnome을 약 6개월 정도 써보고 기타 다른 Linux를 깔면서 겪은 각종 문제들을 정리해둔 것입니다. 아마 계속 업데이트 될 거 같네요.


1. 부팅 문제

부팅이 실패하는 경우는 원인이 무진장 많습니다. 그러나 제가 경험해보니 원인은 거의 하나에 편중되어 있는 거 같더라고요. 그 원인을 순위별로 나타내서 해결법까지 말씀드려보겠습니다.


- 1위. 그래픽 문제

특히, Nvidia 그래픽 카드(왜 리누즈 토르발스가 Nvidia를 욕하는지 알 수 있는 경우입니다)를 쓰는 유저의 경우 처음에 OS를 깔 때 아예 Live CD 조차 GUI 부팅이 안 되는 경우가 있습니다. 또는 데스크탑에서 리눅스 잘 쓰다가 각종 패키지를 업데이트했을 경우(특히 아마 유저가 개발자라면 더 심할 거 같습니다) 기존의 리눅스 그래픽 드라이버와 충돌이 나는지 부팅이 안 되는 경우가 있습니다. 이런 경우에는 제 경험상 아래와 같은 현상이 나타납니다.


    - 1. Live CD로 부팅할 때 리눅스 펭귄 8마리 정도(모니터 크기, 해상도에 따라 다를 겁니다)가 떠 있고 그 이후 어떠한 그래픽의 변화도 일어나지 않는다.

        ↳ CentOS Live CD로 부팅시 발생했습니다.

    - 2. 부팅 로그가 뜨다가 'ACPI PCC Probe Failed' 등 probe 뭐시기가 실패했다, 오류가 났다는 등의 로그가 뜨고 진행이 안 된다.

        ↳ 거의 웬만한 Linux를 깔거나 깔려있는 상태에서 부팅할 때 발생했습니다.

    - 3. 부팅은 정상적으로 된 거 같은데 정확한 계정 정보로 로그인을 시도했음에도 불구하고 로그인 화면이 끊임없이 반복되고 메인 화면으로 이동할 수 없다.

        ↳ Ubuntu Unity에서 Nvidia Driver를 깐 직후 또는 특정 상황(업데이트를 했던가 등)이후에 재부팅했을 경우 발생했습니다.

    - 4. 부팅 화면이 정상적으로 뜨다가 로그인 화면이 나와야 하는데 모니터 백라이트가 깜빡거리면서 정상적으로 GUI 화면이 뜨지 않는 경우

        ↳ Ubuntu Gnome에서 Nvidia Driver를 깐 이후 특정 상황(업데이트를 했던가 등) 이후에 재부팅했을 경우 발생했습니다.


실제로 위의 상황들은 아마 Nvidia 그래픽카드를 쓰시면서 업데이트를 자주하시거나 저처럼 개발자이시면 꽤 많이 만나 볼 수 있지 않을까 싶습니다. 해결 방안은 다음과 같습니다.


    - 1,2번의 경우에는 grub 창(Ubuntu, Advanced Options, Memory Test 등의 옵션이 있고 방향키로 옵션을 선택할 수 있는 화면)에서 원하는 옵션

       (맨 위에 있는 디폴트 옵션 추천)에 선택을 둔 다음에 엔터 누르시지 마시고 e(edit)를 누르셔서 명령어를 수정하셔야 합니다. edit 창 잘 들어오셨으면

       각종 부팅을 위한 grub 명령어들이 모여있는 창이 뜰텐데 여기서 quiet splash 있는 줄에서 splash 뒤에 적절한 곳에 nomodeset을 넣어주시면 됩니

       다. 그리고 나서 F10 또는 Ctrl+X를 통해 부팅을 시도하시면 일단 부팅은 제대로 될 것입니다. 이후에 grub 옵션을 따로 건드셔서 그 옵션에

       nomodeset을 넣어주시면 추가적으로 큰 문제는 발생하지 않으실 겁니다.

    - 3번의 경우엔 Nvidia Driver를 깔 때 Driver 내부에 있는 OpenGL도 같이 설치하셨거나 아니면 업데이트로 인해 드라이버가 충돌난 것으로 보입니다.

       일단 로그인 창까지 오셨으면 Alt+Shift+F1을 누르셔서 CLI 콘솔 모드로 접속하셔야 합니다. 이후에는 계정 정보 입력하시고 Nvidia Graphic Driver

       Installer 설치하시던 것처럼 그대로 설치 진행하시면 됩니다. 이때 OpenGL 옵션이 보인다면 No를 선택하셔서 설치를 막으셔야 합니다. 경험상 apt 등

       으로 설치한 Nvidia Driver는 꽤나 충돌이 잘 나고 문제가 많았기 때문에 저는 Nvidia 공식 홈페이지에서 제공하는 .run 확장자의 드라이버를 추천드립

       니다. .run 확장자의 드라이버를 까는 방법은 콘솔 모드로 들어가셔서 Ubuntu Unity의 경우 'sudo systemctl stop lightdm', Ubuntu Gnome의

       경우 'sudo systemctl stop gdm'으로 GUI 서비스 죽인 후 root 계정으로 드라이버 파일을 실행해서 설치하시면 됩니다. 시간이 나면 나중에 싹 다 정리

       해서 리눅스에서 그래픽카드 드라이버 설치 방법도 안내해드리겠습니다.

    - 4번의 경우엔 일단 GUI 조차 제대로 뜨지 않았고, 게다가 제 컴에서만 그러는지는 모르겠는데 Alt+Shift+F1 등이 먹히지도 않아서 CLI 콘솔 창으로 접속

       이 불가능하더라고요. 그래서 먼저 재부팅을 하신 다음에 grub 창으로 가야합니다. 만약에 grub창이 뜨지 않으시면 리눅스가 부팅화면 뜨기 직전(저 같

       은 경우엔 부팅 화면 직전에 '-'이 깜빡거리는데 그때)부터 grub 옵션창이 뜨기 전까지 Shift 키를 그대로 쭉 누르고 있으면 grub 창이 뜹니다. 여기서 맨

       위에 디폴트 옵션 가시고 e(edit)를 누르셔서 명령어를 수정하셔야 합니다. 명령어 수정창이 뜨면 거기서 splash 있는 줄까지 오신 다음에 'quiet splash

       vt.handoff=7(또는 $~handoff 있는 명령어)'(아마 이 세 개가 다 연속적으로 있을 겁니다.)를 다 지우시고 거기에 'single'을 입력하시고 Ctrl+X 또는

       F10으로 부팅하시면 콘솔창이 뜹니다. 이후엔 위에서 설명한 Nvidia Driver 까는 방법을 그대로 따라하셔서 드라이버를 다시 까시면 됩니다.


- 2위. 디스크 파티션 문제

저 같은 경우엔 NTFS의 하드를 부팅 시에 fstab을 통해 자동 마운트를 시키는데 이게 윈도를 쓰다가 리눅스 부팅할 때 가끔씩 디스크 내부에서 꼬이는 건지 마운트 자체가 에러가 나서 fstab도 오류나고 이로 인해 부팅 자체가 멈춰버리는 경우가 있습니다. 이런 경우에는 제 경험상 아래와 같은 현상이 나타납니다.


    - 1. 부팅화면 잘 뜨고 프로그래스 바 같은 것도 잘 돌아가는 등 그래픽이 정상적으로 동작하다가 어느 순간 멈춰버리는 경우

    - 2. 부팅하다가 또는 부팅할 때 로그를 켜뒀을 때 부팅 오류라면서 Emergency Mode로 들어가는 경우


뭐 저야 이게 파티션이 망가진 게 아니고 일종의 버그 같은 거라서 저는 되게 쉽게 해결했지만 아닌 분들도 있게 추가적으로 제가 아는 한에서 해결 방안을 더 알려드리겠습니다. 1,2번에 대한 해결 방안은 아래과 같습니다.


    - grub 창에 들어가서(자동으로 안 뜨면 부팅 할 때 Shift 키 누르고 있기) Advanced Options -> 리눅스 커널 버전-recovery mode에 들어가시면

       부팅이 됩니다. 그 다음에 잘 보시면 파티션 오류 잡는 거 있는데 그거 실행해주시면 아마 될 겁니다.(이건 제가 진짜 파티션이 망가져 본 적이 없어서 제대

       로 해본 적이 없긴 하네요.

    - 저 같이 리눅스 기본 파티션 말고도 다른 것을 추가적으로 fstab으로 자동 마운트하시고 윈도와 함께 듀얼 부팅하신다면 그냥 윈도 다시 한 번 부팅하셨다

       가 뭐 이대로 끝내기는 좀 아쉬우니 문제있는 해당 디스크 윈도에서 잘 인식되는지 확인하시고 디스크를 탐색기로도 열어보시고 다시 리눅스 들어가시면

       됩니다(...).


2. 사용중 문제

그래도 윈도나 맥보다는 안정성이 조금 떨어져서 은근 문제를 많이 겪었으나 딱히 그렇게 굵직하게 사용상에 큰 지장을 주는 경우는 많이 못 봤습니다. 제가 경험하고 해결한 문제들만 보여드리겠습니다.


- 1. 키보드 & 마우스가 사용 중에 갑자기 멈춰버린다.

↳ 답이 없습니다(...). 다른 USB 포트에 키보드와 마우스를 끼워보시고, 그래도 안 살아난다면 강제로 재부팅을 하는 수 밖에 없습니다. 아마 고질적으로 가끔

     씩 Ubuntu에서 발생하는 문제로 보이는데 저도 아직까지 완벽한 해결책은 찾지 못했습니다.

- 2. Firefox를 실행하는데 분명히 다른 Firefox 프로세스가 실행되고 있지 않음에도 제대로 실행할 수 없다.

↳ 이건 설치하고 초기 실행할 때 제대로 안 했기 때문에 그럽니다. 사실 apt 등으로 firefox를 설치하신다면 죽어도 만날 수 없는 오류라고 전 생각합니다. 저

     같은 경우는 처음에 Firefox Developer Edition을 /opt/폴더 내부에 옮겨서 설치한 후에 'root' 권한으로 Firefox를 실행했는데 이때 초기 설정 파일들

     이 'root' 권한으로 생성되어서 이후에 일반 'user' 권한으로 기존의 Firefox를 실행하면 그 파일들을 불러오지 못해서 발생하는 오류라고 봅니다.

     (Chrome/Chromium은 이걸 알고 있었는지 'root' 계정으로는 죽어도 실행 못하게 만들었더라고요. 왜 그런가 했더니 이런 빅픽쳐가...)  해결방안은

     먼저 firefox를 지우시고(Developer Edition은 그 폴더 자체를 삭제) 이후에는 아래 폴더들을 완벽하게 삭제하시고 재설치하시면 됩니다.

     ~/.mozilla/firefox/

     /etc/firefox/

     /usr/lib/firefox/

     /usr/lib/firefox-addons/

+ Recent posts

티스토리 툴바